aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/include
diff options
context:
space:
mode:
authorJohn "Lameguy" Wilbert Villamor <lameguy64@gmail.com>2022-11-03 10:14:22 +0800
committerGitHub <noreply@github.com>2022-11-03 10:14:22 +0800
commit4139331d233b7a962e747c5564fa68a285f81cc8 (patch)
treed4d3374afd5e36e8580cc424ab2c63ee9e7d357c /libpsn00b/include
parente08a3d9366f8ca14a76b3dd569dac1fb9f569748 (diff)
parent37d963f724113e45d15aa9b8ee86baa9c4362b8f (diff)
downloadpsn00bsdk-4139331d233b7a962e747c5564fa68a285f81cc8.tar.gz
Merge pull request #60 from spicyjpeg/bugfix
Bugfixes, new serial port API and sound examples
Diffstat (limited to 'libpsn00b/include')
-rw-r--r--libpsn00b/include/assert.h23
-rw-r--r--libpsn00b/include/dlfcn.h237
-rw-r--r--libpsn00b/include/hwregs_a.inc22
-rw-r--r--libpsn00b/include/hwregs_c.h21
-rw-r--r--libpsn00b/include/inline_c.h173
-rw-r--r--libpsn00b/include/psxcd.h828
-rw-r--r--libpsn00b/include/psxetc.h196
-rw-r--r--libpsn00b/include/psxgpu.h105
-rw-r--r--libpsn00b/include/psxgte.h205
-rw-r--r--libpsn00b/include/psxpress.h158
-rw-r--r--libpsn00b/include/psxsio.h319
-rw-r--r--libpsn00b/include/psxspu.h39
-rw-r--r--libpsn00b/include/stdlib.h12
13 files changed, 1926 insertions, 412 deletions
diff --git a/libpsn00b/include/assert.h b/libpsn00b/include/assert.h
index 32301e2..12212af 100644
--- a/libpsn00b/include/assert.h
+++ b/libpsn00b/include/assert.h
@@ -1,20 +1,35 @@
/*
- * PSn00bSDK assert macro
+ * PSn00bSDK assert macro and internal logging
* (C) 2022 spicyjpeg - MPL licensed
+ *
+ * Note that the _sdk_log() macro is used internally by PSn00bSDK to output
+ * debug messages and warnings.
*/
#ifndef __ASSERT_H
#define __ASSERT_H
+#include <stdio.h>
+
void _assert_abort(const char *file, int line, const char *expr);
#ifdef NDEBUG
-#define assert(x)
+
+#define assert(expr)
+#define _sdk_log(fmt, ...)
+
#else
+
#define assert(expr) { \
- if (!(expr)) \
- _assert_abort(__FILE__, __LINE__, #expr); \
+ if (!(expr)) _assert_abort(__FILE__, __LINE__, #expr); \
}
+
+#ifdef SDK_LIBRARY_NAME
+#define _sdk_log(fmt, ...) printf(SDK_LIBRARY_NAME ": " fmt, ##__VA_ARGS__)
+#else
+#define _sdk_log(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#endif
+
#endif
#endif
diff --git a/libpsn00b/include/dlfcn.h b/libpsn00b/include/dlfcn.h
index 5848a95..3c5260d 100644
--- a/libpsn00b/include/dlfcn.h
+++ b/libpsn00b/include/dlfcn.h
@@ -7,38 +7,31 @@
#define __DLFCN_H
#include <stdint.h>
+#include <stddef.h>
#include <elf.h>
-/* Helper macro for setting $t9 before calling a function */
+/* Macros */
-#define DL_PRE_CALL(func) { \
- __asm__ volatile("move $t9, %0;" :: "r"(func) : "$t9"); \
-}
+/**
+ * @brief Prepares for a DLL function call.
+ *
+ * @details Sets the $t9 register to the specified value (which should be a
+ * pointer to a DLL function obtained using DL_GetDLLSymbol()). This must be
+ * done prior to calling a DLL function from the main executable to ensure the
+ * DLL can correctly invoke the symbol resolver if necessary.
+ *
+ * This macro is not required when calling a DLL function from another DLL, as
+ * GCC will generate code to set $t9 appropriately.
+ */
+#define DL_PRE_CALL(func) \
+ __asm__ volatile("move $t9, %0;" :: "r"(func) : "$t9");
-/* Types */
-
-#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_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;
+/* Structure and enum definitions */
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_FREE_ON_DESTROY = 4 // Automatically free DLL buffer when closing DLL
+ DL_LAZY = 1, // Resolve functions when they are first called (default)
+ DL_NOW = 2, // Resolve all symbols immediately on load
+ DL_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
@@ -55,151 +48,171 @@ typedef struct _DLL {
uint16_t got_length;
} DLL;
-/* API */
+/* Public API */
#ifdef __cplusplus
extern "C" {
#endif
/**
- * @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
- * private hash table; the buffer won't be further referenced and can be safely
- * deallocated after parsing. Returns the number of entries successfully parsed
- * or -1 if an error occurred.
+ * @brief Creates an empty symbol map in memory.
*
- * This function expects the string buffer to contain one more lines, each of
- * which must follow this format:
+ * @details Initializes the internal symbol hash table to contain at most the
+ * given number of symbols. Once this function is called, symbols can be
+ * registered using DL_AddMapSymbol() and then looked up using
+ * DL_GetMapSymbol(). The default DLL resolver will search the hash table for
+ * external symbols required by DLLs.
*
- * <SYMBOL_NAME> <T|R|D|B> <HEX_ADDRESS> <HEX_SIZE> [DEBUG_INFO...]
+ * This function is normally not required when loading a map file through
+ * DL_ParseSymbolMap(), but it can be used alongside DL_AddMapSymbol() to
+ * implement a custom symbol map parser.
*
- * The "nm" tool included in the GCC toolchain can be used to generate a map
- * file in the appropriate format after building the executable, by using this
- * command:
+ * @param num_entries
+ * @return 0 or -1 in case of error
*
- * mipsel-none-elf-nm -f posix -l -n executable.elf >executable.map
+ * @see DL_AddMapSymbol(), DL_GetMapSymbol()
+ */
+int DL_InitSymbolMap(int num_entries);
+
+/**
+ * @brief Destroys the currently loaded symbol map.
*
- * @param ptr
- * @param size
- * @return -1 or number of entries parsed
+ * @details Frees the internal hash table allocated by DL_InitSymbolMap() or
+ * DL_ParseSymbolMap(), containing the currently loaded symbol map. Freeing the
+ * table manually before loading a new symbol map is normally unnecessary as it
+ * is done automatically, however this function can be useful to recover heap
+ * space once the map is no longer needed.
*/
-int32_t DL_ParseSymbolMap(const char *ptr, size_t size);
+void DL_UnloadSymbolMap(void);
/**
- * @brief File wrapper around DL_ParseSymbolMap(). Allocates a temporary buffer
- * then loads the specified map file into it (using BIOS APIs) and calls
- * DL_ParseSymbolMap() to parse it. The buffer is deallocated immediately after
- * parsing.
+ * @brief Adds a symbol to the currently loaded symbol map.
*
- * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1"
- * @return -1 or number of entries parsed
+ * @details Registers a new symbol (function or variable) with the given name
+ * and address, and adds it to the internal hash table. The symbol can then be
+ * looked up using DL_GetMapSymbol(). The default DLL resolver will search the
+ * hash table for external symbols required by DLLs.
+ *
+ * This function shall only be called after DL_InitSymbolMap() or
+ * DL_ParseSymbolMap() is called.
+ *
+ * @param name
+ * @param ptr
+ *
+ * @see DL_GetMapSymbol()
*/
-//int32_t DL_LoadSymbolMapFromFile(const char *filename);
+void DL_AddMapSymbol(const char *name, void *ptr);
/**
- * @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.
+ * @brief Creates a symbol map in memory from a map file in text format.
+ *
+ * @details Initializes the internal symbol hash table, then parses entries
+ * from the provided string buffer (which may or may not be null-terminated)
+ * and adds each one to the table. The string buffer won't be further
+ * referenced and can be safely deallocated after parsing. Returns the number
+ * of entries successfully parsed.
+ *
+ * The string buffer shall contain one or more lines, each of which must follow
+ * this format:
+ *
+ * <SYMBOL_NAME> <T|R|D|B> <HEX_ADDRESS> <HEX_SIZE> [...]
+ *
+ * The "nm" tool included in the GCC toolchain can be used to generate a map
+ * file in the appropriate format after building the executable:
+ *
+ * mipsel-none-elf-nm -f posix -l -n executable.elf >executable.map
+ *
+ * @param ptr
+ * @param size
+ * @return Number of entries parsed, -1 in case of failure
+ *
+ * @see DL_UnloadSymbolMap(), DL_GetMapSymbol()
*/
-void DL_UnloadSymbolMap(void);
+int DL_ParseSymbolMap(const char *ptr, size_t size);
/**
- * @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
- * the symbol. If the symbol can't be found, null is returned instead.
+ * @brief Gets a pointer to a symbol in the currently loaded map by its name.
+ *
+ * @details 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 the symbol. If the symbol can't be found, a null pointer is returned.
*
* @param name
- * @return NULL or pointer to symbol (any type)
+ * @return NULL or pointer to symbol
*/
-void *DL_GetSymbolByName(const char *name);
+void *DL_GetMapSymbol(const char *name);
/**
- * @brief Sets a custom function to be called for resolving symbols in DLLs.
+ * @brief Sets a custom handler for resolving symbols in DLLs.
+ *
+ * @details 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
* symbol's name, and should return the address of the symbol in the executable
* (the dynamic linker will lock up if it returns null). Passing null instead
- * of a function resets the default behavior of calling DL_GetSymbolByName() to
+ * of a function resets the default behavior of calling DL_GetMapSymbol() to
* find the symbol in the currently loaded symbol map.
- *
+ *
* @param callback NULL or pointer to callback function
+ * @return Previously set callback or NULL
*/
-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
+ * @brief Initializes a DLL structure.
+ *
+ * @details Initializes a buffer holding the contents of a dynamically-loaded
* library file (compiled with the dll.ld linker script and converted to a raw
- * binary) *in-place*. A new DLL struct is allocated to store metadata but,
+ * binary) *in-place*. Metadata is written to the provided DLL struct 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 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
- * they are first called, while RTLD_NOW forces all symbols to be resolved
- * immediately. If a custom resolver has been set via DL_SetResolveCallback(),
- * it will be called for each symbol to resolve.
+ * Setting it to DL_LAZY defers resolution of undefined functions to when they
+ * are first called, while DL_NOW forces all symbols to be resolved
+ * immediately. Either mode can be OR'd with DL_FREE_ON_DESTROY to
+ * automatically deallocate the provided buffer when DL_DestroyDLL() is called.
*
+ * If a custom resolver has been set via DL_SetResolveCallback(), it will be
+ * called for each symbol to resolve.
+ *
+ * @param dll
* @param ptr
* @param size
- * @param mode RTLD_LAZY or RTLD_NOW
- * @return NULL or pointer to a new DLL struct
- */
-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
- * calling dlclose() on a DLL loaded from a file, the file buffer is
- * automatically destroyed.
- *
- * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1"
- * @param mode RTLD_LAZY or RTLD_NOW + optionally RTLD_FREE_ON_DESTROY
- * @return NULL or pointer to a new DLL struct
+ * @param mode DL_LAZY or DL_NOW, optionally with DL_FREE_ON_DESTROY
+ * @return Pointer to DLL structure or NULL in case of failure
+ *
+ * @see DL_DestroyDLL(), DL_GetDLLSymbol()
*/
-//DLL *DL_LoadDLLFromFile(const char *filename, DL_ResolveMode mode);
+DLL *DL_CreateDLL(DLL *dll, void *ptr, size_t size, 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 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.
+ * @brief Destroys a DLL structure.
+ *
+ * @details Destroys a loaded DLL by calling its global destructors. If the DLL
+ * was initialized with the DL_FREE_ON_DESTROY flag, the buffer associated with
+ * the DLL is also deallocated. Note that the DLL structure itself is *not*
+ * deallocated.
*
* @param 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
- * executable itself is searched instead using the symbol map (behaving the
- * same as DL_GetSymbolByName()).
+ * @brief Gets a pointer to a symbol in a DLL by its name.
+ *
+ * @details Returns a pointer to the DLL symbol with the given name, or null if
+ * it can't be found. If a null pointer is passed as first argument, the
+ * executable itself is searched instead using the symbol map (behaving
+ * identically to DL_GetMapSymbol()).
*
- * @param dll DLL struct or RTLD_DEFAULT
+ * @param dll Pointer to DLL structure or NULL
* @param name
* @return NULL or pointer to symbol (any type)
*/
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
- * also resets the internal error flags).
- *
- * @return NULL or member of DL_Error enum
- */
-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
}
#endif
diff --git a/libpsn00b/include/hwregs_a.inc b/libpsn00b/include/hwregs_a.inc
index c78b41a..ca38542 100644
--- a/libpsn00b/include/hwregs_a.inc
+++ b/libpsn00b/include/hwregs_a.inc
@@ -32,12 +32,18 @@
.set SPU_MASTER_VOL_R, 0x1d82
.set SPU_REVERB_VOL_L, 0x1d84
.set SPU_REVERB_VOL_R, 0x1d86
-.set SPU_KEY_ON, 0x1d88
-.set SPU_KEY_OFF, 0x1d8c
-.set SPU_FM_MODE, 0x1d90
-.set SPU_NOISE_MODE, 0x1d94
-.set SPU_REVERB_ON, 0x1d98
-.set SPU_CHAN_STATUS, 0x1d9c
+.set SPU_KEY_ON1, 0x1d88
+.set SPU_KEY_ON1, 0x1d8a
+.set SPU_KEY_OFF1, 0x1d8c
+.set SPU_KEY_OFF2, 0x1d8e
+.set SPU_FM_MODE1, 0x1d90
+.set SPU_FM_MODE2, 0x1d92
+.set SPU_NOISE_MODE1, 0x1d94
+.set SPU_NOISE_MODE2, 0x1d96
+.set SPU_REVERB_ON1, 0x1d98
+.set SPU_REVERB_ON2, 0x1d9a
+.set SPU_CHAN_STATUS1, 0x1d9c
+.set SPU_CHAN_STATUS2, 0x1d9e
.set SPU_REVERB_ADDR, 0x1da2
.set SPU_IRQ_ADDR, 0x1da4
@@ -59,8 +65,8 @@
.set SPU_VOICE_VOL_R, 0x02
.set SPU_VOICE_FREQ, 0x04
.set SPU_VOICE_ADDR, 0x06
-.set SPU_VOICE_ADSR_L, 0x08
-.set SPU_VOICE_ADSR_H, 0x0a
+.set SPU_VOICE_ADSR1, 0x08
+.set SPU_VOICE_ADSR2, 0x0a
.set SPU_VOICE_LOOP, 0x0e
## MDEC
diff --git a/libpsn00b/include/hwregs_c.h b/libpsn00b/include/hwregs_c.h
index b205b87..0e21922 100644
--- a/libpsn00b/include/hwregs_c.h
+++ b/libpsn00b/include/hwregs_c.h
@@ -38,12 +38,18 @@
#define SPU_MASTER_VOL_R _MMIO16(IOBASE | 0x1d82)
#define SPU_REVERB_VOL_L _MMIO16(IOBASE | 0x1d84)
#define SPU_REVERB_VOL_R _MMIO16(IOBASE | 0x1d86)
-#define SPU_KEY_ON _MMIO32(IOBASE | 0x1d88)
-#define SPU_KEY_OFF _MMIO32(IOBASE | 0x1d8c)
-#define SPU_FM_MODE _MMIO32(IOBASE | 0x1d90)
-#define SPU_NOISE_MODE _MMIO32(IOBASE | 0x1d94)
-#define SPU_REVERB_ON _MMIO32(IOBASE | 0x1d98)
-#define SPU_CHAN_STATUS _MMIO32(IOBASE | 0x1d9c)
+#define SPU_KEY_ON1 _MMIO16(IOBASE | 0x1d88)
+#define SPU_KEY_ON2 _MMIO16(IOBASE | 0x1d8a)
+#define SPU_KEY_OFF1 _MMIO16(IOBASE | 0x1d8c)
+#define SPU_KEY_OFF2 _MMIO16(IOBASE | 0x1d8e)
+#define SPU_FM_MODE1 _MMIO16(IOBASE | 0x1d90)
+#define SPU_FM_MODE2 _MMIO16(IOBASE | 0x1d92)
+#define SPU_NOISE_MODE1 _MMIO16(IOBASE | 0x1d94)
+#define SPU_NOISE_MODE2 _MMIO16(IOBASE | 0x1d96)
+#define SPU_REVERB_ON1 _MMIO16(IOBASE | 0x1d98)
+#define SPU_REVERB_ON2 _MMIO16(IOBASE | 0x1d9a)
+#define SPU_CHAN_STATUS1 _MMIO16(IOBASE | 0x1d9c)
+#define SPU_CHAN_STATUS2 _MMIO16(IOBASE | 0x1d9e)
#define SPU_REVERB_ADDR _MMIO16(IOBASE | 0x1da2)
#define SPU_IRQ_ADDR _MMIO16(IOBASE | 0x1da4)
@@ -67,7 +73,8 @@
#define SPU_CH_VOL_R(N) _MMIO16(IOBASE | 0x1c02 + 16 * (N))
#define SPU_CH_FREQ(N) _MMIO16(IOBASE | 0x1c04 + 16 * (N))
#define SPU_CH_ADDR(N) _MMIO16(IOBASE | 0x1c06 + 16 * (N))
-#define SPU_CH_ADSR(N) _MMIO32(IOBASE | 0x1c08 + 16 * (N))
+#define SPU_CH_ADSR1(N) _MMIO16(IOBASE | 0x1c08 + 16 * (N))
+#define SPU_CH_ADSR2(N) _MMIO16(IOBASE | 0x1c0a + 16 * (N))
#define SPU_CH_LOOP_ADDR(N) _MMIO16(IOBASE | 0x1c0e + 16 * (N))
/* MDEC */
diff --git a/libpsn00b/include/inline_c.h b/libpsn00b/include/inline_c.h
index c5eaa59..5facc1c 100644
--- a/libpsn00b/include/inline_c.h
+++ b/libpsn00b/include/inline_c.h
@@ -3,9 +3,17 @@
* (C) 2019 Lameguy64
* (C) 2021-2022 Soapy (tweaked by spicyjpeg)
*
- * This header is basically identical to Nugget's inline_n.h. All GTE commands
- * can be used right away without having to run DMPSX or any other tool on
- * object files.
+ * This header is basically identical to Nugget's inline_n.h.
+ */
+
+/**
+ * @file inline_c.h
+ * @brief Inline GTE macro header
+ *
+ * @details This header provides a set of macros for making use of GTE commands
+ * and registers from C or C++ code. Unlike the official SDK, all commands can
+ * be used right away without having to run any other post-processing tool on
+ * compiled object files.
*/
#ifndef _INLINE_C_H
@@ -13,7 +21,11 @@
/* GTE load macros */
-/* Load a SVECTOR (passed as a pointer) to GTE V0
+/**
+ * @brief Loads a single SVECTOR to GTE vector register V0
+ *
+ * @details Loads values from an SVECTOR struct to GTE data registers C2_VXY0
+ * and C2_VZ0.
*/
#define gte_ldv0( r0 ) __asm__ volatile ( \
"lwc2 $0, 0( %0 );" \
@@ -22,7 +34,11 @@
: "r"( r0 ) \
: "$t0" )
-/* Load a SVECTOR (passed as a pointer) to GTE V1
+/**
+ * @brief Loads a single SVECTOR to GTE vector register V1
+ *
+ * @details Loads values from an SVECTOR struct to GTE data registers C2_VXY1
+ * and C2_VZ1.
*/
#define gte_ldv1( r0 ) __asm__ volatile ( \
"lwc2 $2, 0( %0 );" \
@@ -31,7 +47,11 @@
: "r"( r0 ) \
: "$t0" )
-/* Load a SVECTOR (passed as a pointer) to GTE V2
+/**
+ * @brief Loads a single SVECTOR to GTE vector register V2
+ *
+ * @details Loads values from an SVECTOR struct to GTE data registers C2_VXY2
+ * and C2_VZ2.
*/
#define gte_ldv2( r0 ) __asm__ volatile ( \
"lwc2 $4, 0( %0 );" \
@@ -40,7 +60,11 @@
: "r"( r0 ) \
: "$t0" )
-/* Load three SVECTORs (passed as a pointer) to the GTE at once
+/**
+ * @brief Load three SVECTORs to GTE vector registers at once
+ *
+ * @details Loads values from three SVECTOR structs to GTE data registers
+ * C2_VXY0 and C2_VZ0, C2_VXY1 and C2_VZ1, C2_VXY2 and C2_VZ2 at once.
*/
#define gte_ldv3( r0, r1, r2 ) __asm__ volatile ( \
"lwc2 $0, 0( %0 );" \
@@ -88,6 +112,14 @@
: \
: "r"( r0 ) )
+/**
+ * @brief Load a CVECTOR to GTE register C2_RGBC
+ *
+ * @details Loads a CVECTOR value to GTE data register C2_RGBC. The primitive
+ * code (the last byte of a CVECTOR) is passed to the color FIFO registers when
+ * performing lighting compute operations, so it can be stored to the RGBC
+ * field of a primitive directly without any additional operation required.
+ */
#define gte_ldrgb( r0 ) __asm__ volatile ( \
"lwc2 $6 , 0( %0 );" \
: \
@@ -224,6 +256,12 @@
: "r"( r0 ) \
: "$12", "$13", "$14" )
+/**
+ * @brief Loads values to GTE registers C2_IR1-3
+ *
+ * @details Loads three 32-bit values to GTE data registers C2_IR1, C2_IR2 and
+ * C2_IR3.
+ */
#define gte_ldopv2( r0 ) __asm__ volatile ( \
"lwc2 $11, 8( %0 );" \
"lwc2 $9 , 0( %0 );" \
@@ -253,6 +291,14 @@
: \
: "r"( r0 ), "r"( r1 ), "r"( r2 ) )
+/**
+ * @brief Sets an RGB color value to the GTE
+ *
+ * @details Sets the specified RGB value to GTE control registers C2_RBK,
+ * C2_GBK and C2_BBK. This specifies the color value to use when a normal faces
+ * away from the direction of the light source. This can be considered as the
+ * ambient light color.
+ */
#define gte_SetBackColor( r0, r1, r2 ) __asm__ volatile ( \
"sll $t0, %0, 4;" \
"sll $t1, %1, 4;" \
@@ -282,6 +328,13 @@
: "r"( r0 ), "r"( r1 ), "r"( r2 ) \
: "$12", "$13", "$14" )
+/**
+ * @brief Sets the GTE screen offset
+ *
+ * @details Sets the values of the GTE screen offset which is applied to 2D
+ * projected coordinates when performing perspective transformation. The values
+ * are set to GTE control registers C2_OFX and C2_OFY.
+ */
#define gte_SetGeomOffset( r0, r1 ) __asm__ volatile ( \
"sll $t0, %0, 16;" \
"sll $t1, %1, 16;" \
@@ -291,6 +344,13 @@
: "r"( r0 ), "r"( r1 ) \
: "$t0", "$t1" )
+/**
+ * @brief Sets the distance of the projection plane
+ *
+ * @details Sets the specified value to GTE control register C2_H which
+ * determines the projection plane distance, otherwise known as the field of
+ * view.
+ */
#define gte_SetGeomScreen( r0 ) __asm__ volatile ( \
"ctc2 %0, $26;" \
: \
@@ -305,6 +365,12 @@
: "r"( r0 ) \
: "$12", "$13" )
+/**
+ * @brief Sets a 3x3 rotation matrix portion from a MATRIX to the GTE
+ *
+ * @details Sets the 3x3 rotation matrix coordinates from a MATRIX struct to
+ * GTE control registers C2_R11R12, C2_R13R21, C2_R22R23, C2_R31R32 and C2_R33.
+ */
#define gte_SetRotMatrix( r0 ) __asm__ volatile ( \
"lw $t0, 0( %0 );" \
"lw $t1, 4( %0 );" \
@@ -329,6 +395,17 @@
: "r"( r0 ) \
: "$12", "$13" )
+/**
+ * @brief Sets a 3x3 lighting matrix from a MATRIX to the GTE
+ *
+ * @details Sets the 3x3 lighting matrix coordinates from a MATRIX struct to
+ * GTE control registers C2_L11L12, C2_L13L21, C2_L22L23, C2_L31L32 and C2_L33.
+ *
+ * The lighting matrix is essentially a triplet of three light direction
+ * vectors. L11, L12 and L13 represents the X, Y and Z coordinates of light
+ * source 0 for example. Coordinates must be normalized to ensure correct
+ * results.
+ */
#define gte_SetLightMatrix( r0 ) __asm__ volatile ( \
"lw $t0, 0( %0 );" \
"lw $t1, 4( %0 );" \
@@ -353,6 +430,17 @@
: "r"( r0 ) \
: "$12", "$13" )
+/**
+ * @brief Sets a 3x3 color matrix from a MATRIX to the GTE
+ *
+ * @details Sets the 3x3 color matrix values from a MATRIX struct to GTE
+ * control registers C2_LR1LR2, C2_LR3LG1, C2_LG2LG3, C2_LB1LB2 and C2_LB3.
+ *
+ * The light color matrix is essentially a triplet of three RGB colors for each
+ * of the three light sources. LR1, LG1 and LB1 represents the R, G and B color
+ * values for light source 0 for example. Values are of range 0 to 4095, higher
+ * values will be saturated.
+ */
#define gte_SetColorMatrix( r0 ) __asm__ volatile ( \
"lw $t0, 0( %0 );" \
"lw $t1, 4( %0 );" \
@@ -368,6 +456,12 @@
: "r"( r0 ) \
: "$t2" )
+/**
+ * @brief Sets the translation portion of a MATRIX to the GTE
+ *
+ * @details Sets the translation coordinates from a MATRIX struct to GTE
+ * control registers C2_TRX, C2_TRY and C2_TRZ respectively.
+ */
#define gte_SetTransMatrix( r0 ) __asm__ volatile ( \
"lw $t0, 20( %0 );" \
"lw $t1, 24( %0 );" \
@@ -1044,11 +1138,39 @@
/* GTE operation macros */
+/**
+ * @brief Rotate, Translate and Perspective Single (15 cycles)
+ *
+ * @details Performs rotation, translation and perspective calculation of a
+ * single vertex. Divide overflows are simply saturated allowing for crude Z
+ * clipping. Check C2_FLAG to determine which overflow error has occurred
+ * during calculation.
+ *
+ * The following equation is performed when executing this GTE command:
+ *
+ * IR1 = MAC1 = (TRX*4096 + R11*VX0 + R12*VY0 + R13*VZ0) / 4096
+ * IR2 = MAC2 = (TRY*4096 + R21*VX0 + R22*VY0 + R23*VZ0) / 4096
+ * IR3 = MAC3 = (TRZ*4096 + R31*VX0 + R32*VY0 + R33*VZ0) / 4096
+ * SZ3 = MAC3
+ *
+ * MAC0 = (((H*131072/SZ3)+1)/2) * IR1 + OFX, SX2 = MAC0 / 65536
+ * MAC0 = (((H*131072/SZ3)+1)/2) * IR2 + OFY, SY2 = MAC0 / 65536
+ * MAC0 = (((H*131072/SZ3)+1)/2) * DQA + DQB, IR0 = MAC0 / 4096
+ */
#define gte_rtps() __asm__ volatile ( \
"nop;" \
"nop;" \
"cop2 0x0180001;" )
+/**
+ * @brief Rotate, Translate and Perspective Triple (23 cycles)
+ *
+ * @details Performs rotation, translation and perspective calculation of three
+ * vertices at once. The equation performed is the same as gte_rtps() only
+ * repeated three times for each vertex. The result of the first vertex is
+ * stored in GTE data register C2_SXY0, the second vector in C2_SXY1 then
+ * C2_SXY2.
+ */
#define gte_rtpt() __asm__ volatile ( \
"nop;" \
"nop;" \
@@ -1325,16 +1447,53 @@
"nop;" \
"cop2 0x0138041C;" )
+/**
+ * @brief Normal clipping (8 cycles)
+ *
+ * @details Computes the sign of three screen coordinates (C2_SXY0-3) used for
+ * backface culling. If the value of C2_MAC0 is negative, the coordinates are
+ * inverted and thus the triangle is back facing.
+ *
+ * The following equation is performed when executing this GTE command:
+ *
+ * MAC0 = SX0*SY1 + SX1*SY2 + SX2*SY0 - SX0*SY2 - SX1*SY0 - SX2*SY1
+ */
#define gte_nclip() __asm__ volatile ( \
"nop;" \
"nop;" \
"cop2 0x01400006;" )
+/**
+ * @brief Average screen Z result (5 cycles)
+ *
+ * @details Averages the values of GTE registers C2_SZ1, C2_SZ2 and C2_SZ3,
+ * multiplies it by C2_ZSF3 and divides the result by 0x1000 before storing to
+ * C2_OTZ. Used to compute the ordering table depth level for a three-vertex
+ * primitive.
+ *
+ * The following equation is performed when executing this GTE command:
+ *
+ * MAC0 = ZSF3 * (SZ1+SZ2+SZ3)
+ * OTZ = MAC0 / 4096
+ */
#define gte_avsz3() __asm__ volatile ( \
"nop;" \
"nop;" \
"cop2 0x0158002D;" )
+/**
+ * @brief Average screen Z result (6 cycles)
+ *
+ * @details Averages the values of GTE registers C2_SZ1, C2_SZ2, C2_SZ3 and
+ * C2_SZ4, multiplies it by C2_ZSF4 and divides the result by 0x1000 before
+ * storing to C2_OTZ. Used to compute the ordering table depth level for a
+ * four-vertex primitive.
+ *
+ * The following equation is performed when executing this GTE command:
+ *
+ * MAC0 = ZSF4 * (SZ1+SZ2+SZ3+SZ4)
+ * OTZ = MAC0 / 4096
+ */
#define gte_avsz4() __asm__ volatile ( \
"nop;" \
"nop;" \
diff --git a/libpsn00b/include/psxcd.h b/libpsn00b/include/psxcd.h
index 0460f20..8150703 100644
--- a/libpsn00b/include/psxcd.h
+++ b/libpsn00b/include/psxcd.h
@@ -3,174 +3,792 @@
* (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed
*/
+/**
+ * @file psxcd.h
+ * @brief CD-ROM library header
+ *
+ * @details The PSn00bSDK CD-ROM library provides facilities for using the
+ * CD-ROM hardware of the PS1. Unlike the CD-ROM library of the official SDK,
+ * psxcd is immune to the 30 file and directory limit and is capable of parsing
+ * directories containing as many files as the ISO9660 file system can support,
+ * unless the records are too large to be loaded into memory. However, to
+ * maintain compatibility with the PS1 BIOS, the root directory must not exceed
+ * the 30 file limit and the entire disc should contain no more than 45
+ * directories total.
+ *
+ * Whilst psxcd is not constrained by the 30 file per directory limit, it does
+ * not support Joliet CD-ROM extensions to support long file names. However, a
+ * library extension is considered for future development.
+ */
+
#ifndef __PSXCD_H
#define __PSXCD_H
#include <stdint.h>
-/*
- * CD-ROM control commands
- */
-#define CdlNop 0x01 /* a.k.a. Getstat */
-#define CdlSetloc 0x02
-#define CdlPlay 0x03
-#define CdlForward 0x04
-#define CdlBackward 0x05
-#define CdlReadN 0x06
-#define CdlStandby 0x07 /* a.k.a. MotorOn */
-#define CdlStop 0x08
-#define CdlPause 0x09
-#define CdlInit 0x0A
-#define CdlMute 0x0B
-#define CdlDemute 0x0C
-#define CdlSetfilter 0x0D
-#define CdlSetmode 0x0E
-#define CdlGetparam 0x0F
-#define CdlGetlocL 0x10
-#define CdlGetlocP 0x11
-#define CdlSetsession 0x12 /* ORIGINAL CODE */
-#define CdlGetTN 0x13
-#define CdlGetTD 0x14
-#define CdlSeekL 0x15
-#define CdlSeekP 0x16
-#define CdlTest 0x19 /* ORIGINAL CODE */
-#define CdlReadS 0x1B
+/* Enum definitions */
-/*
- * CD-ROM status bits
- */
-#define CdlStatError 0x01
-#define CdlStatStandby 0x02
-#define CdlStatSeekError 0x04
-#define CdlStatIdError 0x08 /* ORIGINAL CODE */
-#define CdlStatShellOpen 0x10
-#define CdlStatRead 0x20
-#define CdlStatSeek 0x40
-#define CdlStatPlay 0x80
+typedef enum _CdlCommand {
+ CdlNop = 0x01,
+ CdlSetloc = 0x02,
+ CdlPlay = 0x03,
+ CdlForward = 0x04,
+ CdlBackward = 0x05,
+ CdlReadN = 0x06,
+ CdlStandby = 0x07,
+ CdlStop = 0x08,
+ CdlPause = 0x09,
+ CdlInit = 0x0a,
+ CdlMute = 0x0b,
+ CdlDemute = 0x0c,
+ CdlSetfilter = 0x0d,
+ CdlSetmode = 0x0e,
+ CdlGetparam = 0x0f,
+ CdlGetlocL = 0x10,
+ CdlGetlocP = 0x11,
+ CdlSetsession = 0x12,
+ CdlGetTN = 0x13,
+ CdlGetTD = 0x14,
+ CdlSeekL = 0x15,
+ CdlSeekP = 0x16,
+ CdlTest = 0x19,
+ CdlGetID = 0x1a,
+ CdlReadS = 0x1b,
+ CdlReset = 0x1c
+} CdlCommand;
-/*
- * CD-ROM mode bits
- */
-#define CdlModeDA 0x01
-#define CdlModeAP 0x02
-#define CdlModeRept 0x04
-#define CdlModeSF 0x08
-//#define CdlModeSize0 0x10
-//#define CdlModeSize1 0x20
-#define CdlModeIgnore 0x10
-#define CdlModeSize 0x20
-#define CdlModeRT 0x40
-#define CdlModeSpeed 0x80
+typedef enum _CdlStatFlag {
+ CdlStatError = 1 << 0,
+ CdlStatStandby = 1 << 1,
+ CdlStatSeekError = 1 << 2,
+ CdlStatIdError = 1 << 3,
+ CdlStatShellOpen = 1 << 4,
+ CdlStatRead = 1 << 5,
+ CdlStatSeek = 1 << 6,
+ CdlStatPlay = 1 << 7
+} CdlStatFlag;
-/*
- * CD-ROM interrupt result values
+typedef enum _CdlModeFlag {
+ CdlModeDA = 1 << 0,
+ CdlModeAP = 1 << 1,
+ CdlModeRept = 1 << 2,
+ CdlModeSF = 1 << 3,
+ //CdlModeSize0 = 1 << 4,
+ //CdlModeSize1 = 1 << 5,
+ CdlModeIgnore = 1 << 4,
+ CdlModeSize = 1 << 5,
+ CdlModeRT = 1 << 6,
+ CdlModeSpeed = 1 << 7
+} CdlModeFlag;
+
+typedef enum _CdlIntrResult {
+ CdlNoIntr = 0,
+ CdlDataReady = 1,
+ CdlComplete = 2,
+ CdlAcknowledge = 3,
+ CdlDataEnd = 4,
+ CdlDiskError = 5
+} CdlIntrResult;
+
+typedef enum _CdlIsoError {
+ CdlIsoOkay = 0,
+ CdlIsoSeekError = 1,
+ CdlIsoReadError = 2,
+ CdlIsoInvalidFs = 3,
+ CdlIsoLidOpen = 4
+} CdlIsoError;
+
+/**
+ * @brief Translates a BCD format value to decimal
+ *
+ * @details Translates a specified value in BCD format (ie. 32/0x20 = 20) into
+ * a decimal integer, as the CD-ROM controller returns integer values only in
+ * BCD format.
*/
-#define CdlNoIntr 0x00
-#define CdlDataReady 0x01
-#define CdlComplete 0x02
-#define CdlAcknowledge 0x03
-#define CdlDataEnd 0x04
-#define CdlDiskError 0x05
+#define btoi(b) ((b)/16*10+(b)%16)
-/*
- * CD-ROM file system error codes (original)
+/**
+ * @brief Translates a decimal value to BCD
+ *
+ * @details Translates a decimal integer into a BCD format value (ie.
+ * 20 = 32/0x20), as the CD-ROM controller only accepts values in BCD format.
*/
-#define CdlIsoOkay 0x00
-#define CdlIsoSeekError 0x01
-#define CdlIsoReadError 0x02
-#define CdlIsoInvalidFs 0x03
-#define CdlIsoLidOpen 0x04
+#define itob(i) ((i)/10*16+(i)%10)
-#define btoi(b) ((b)/16*10+(b)%16) /* Convert BCD value to integer */
-#define itob(i) ((i)/10*16+(i)%10) /* Convert integer to BCD value */
+/* Structure and type definitions */
-/*
- * CD-ROM disc location struct
- */
-typedef struct _CdlLOC
-{
- uint8_t minute;
- uint8_t second;
- uint8_t sector;
- uint8_t track;
+/**
+ * @brief CD-ROM positional coordinates
+ *
+ * @details This structure is used to specify CD-ROM positional coordinates for
+ * CdlSetloc, CdlReadN and CdlReadS CD-ROM commands. Use CdIntToPos() to set
+ * parameters from a logical sector number.
+ *
+ * @see CdIntToPos(), CdControl()
+ */
+typedef struct _CdlLOC {
+ uint8_t minute; // Minutes (BCD)
+ uint8_t second; // Seconds (BCD)
+ uint8_t sector; // Sector or frame (BCD)
+ uint8_t track; // Track number (not used)
} CdlLOC;
-/*
- * CD-ROM audio attenuation struct (volume)
- */
-typedef struct _CdlATV
-{
- uint8_t val0; /* L -> SPU L */
- uint8_t val1; /* L -> SPU R */
- uint8_t val2; /* R -> SPU R */
- uint8_t val3; /* R -> SPU L */
+/**
+ * @brief CD-ROM attenuation parameters
+ *
+ * @details This structure specifies parameters for the CD-ROM attenuation.
+ * Values must be of range 0 to 127.
+ *
+ * The CD-ROM attenuation can be used to set the CD-ROM audio output to mono
+ * (0x40, 0x40, 0x40, 0x40) or reversed stereo (0x00, 0x80, 0x00, 0x80). It can
+ * also be used to play one of two stereo channels to both speakers.
+ *
+ * The CD-ROM attenuation affects CD-DA and CD-XA audio.
+ *
+ * @see CdMix()
+ */
+typedef struct _CdlATV {
+ uint8_t val0; // CD to SPU L-to-L volume
+ uint8_t val1; // CD to SPU L-to-R volume
+ uint8_t val2; // CD to SPU R-to-R volume
+ uint8_t val3; // CD to SPU R-to-L volume
} CdlATV;
-/*
- * CD-ROM file information struct
+/**
+ * @brief File entry structure
+ *
+ * @details Used to store basic information of a file such as logical block
+ * location and size. Currently, CdSearchFile() is the only function that uses
+ * this struct but it will be used in directory listing functions that may be
+ * implemented in the future.
+ *
+ * @see CdSearchFile()
*/
-typedef struct _CdlFILE
-{
- CdlLOC pos;
- uint32_t size;
- char name[16];
+typedef struct _CdlFILE {
+ CdlLOC pos; // CD-ROM position coordinates of file
+ uint32_t size; // Size of file in bytes
+ char name[16]; // File name
} CdlFILE;
-typedef struct _CdlFILTER
-{
- uint8_t file;
- uint8_t chan;
- uint16_t pad;
+/**
+ * @brief Structure used to set CD-ROM XA filter
+ *
+ * @details This structure is used to specify stream filter parameters for
+ * CD-ROM XA audio streaming using the CdlSetfilter command. This only affects
+ * CD-ROM XA audio streaming.
+ *
+ * CD-ROM XA audio is normally comprised of up to 8 or more ADPCM compressed
+ * audio streams interleaved into one continuous stream of data. The data
+ * stream is normally read at 2x speed but only one of eight XA audio streams
+ * can be played at a time. The XA stream to play is specified by the
+ * CdlSetfilter command and this struct.
+ *
+ * The CD-ROM XA filter can be changed during CD-ROM XA audio playback with
+ * zero audio interruption. This can be used to achieve dynamic music effects
+ * by switching to alternate versions of a theme to fit specific scenes
+ * seamlessly.
+ *
+ * @see CdControl()
+ */
+typedef struct _CdlFILTER {
+ uint8_t file; // File number to fetch (usually 1)
+ uint8_t chan; // Channel number (0 through 7)
+ uint16_t pad; // Padding
} CdlFILTER;
-/* Directory query context */
-typedef void* CdlDIR;
+/**
+ * @brief CD-ROM directory query context handle
+ *
+ * @details Used to store a directory context created by CdOpenDir(). An open
+ * context can then be used with CdReadDir() and closed with CdCloseDir().
+ *
+ * @see CdOpenDir()
+ */
+typedef void *CdlDIR;
-/* Data callback */
typedef void (*CdlCB)(int, uint8_t *);
+/* Public API */
+
#ifdef __cplusplus
extern "C" {
#endif
+/**
+ * @brief Initializes the CD-ROM library
+ *
+ * @details Initializes the CD-ROM subsystem which includes hooking the
+ * required IRQ handler, sets up internal variables of the CD-ROM library and
+ * attempts to initialize the CD-ROM controller. The mode parameter does
+ * nothing but may be used in future updates of this library.
+ *
+ * This function must be called after ResetGraph and before any other CD-ROM
+ * library function that interfaces with the CD-ROM controller. This function
+ * may not be called twice as it may cause instability or would just crash.
+ *
+ * @return Always 1. May change in the future.
+ */
int CdInit(void);
+/**
+ * @brief Translates a logical sector number to CD-ROM positional coordinates
+ *
+ * @details This function translates the logical sector number from i to CD-ROM
+ * positional coordinates stored to a CdlLOC struct specified by p. The
+ * translation takes the lead-in offset into account so the first logical
+ * sector begins at 0 and the result will be offset by 150 sectors.
+ *
+ * @param i Logical sector number
+ * @param p Pointer to a CdlLOC structure
+ * @return Pointer to the specified CdlLOC struct plus 150 sectors.
+ */
CdlLOC* CdIntToPos(int i, CdlLOC *p);
-int CdPosToInt(CdlLOC *p);
+
+/**
+ * @brief Translates CD-ROM positional coordinates to a logical sector number
+ *
+ * @details Translates the CD-ROM position parameters from a CdlLOC struct
+ * specified by p to a logical sector number. The translation takes the lead-in
+ * offset of 150 sectors into account so the logical sector number returned
+ * would begin at zero.
+ *
+ * @param p Pointer to a CdlLOC struct
+ * @return Logical sector number minus the 150 sector lead-in.
+ */
+int CdPosToInt(const CdlLOC *p);
+
+/**
+ * @brief Gets CD-ROM TOC information
+ *
+ * @details Retrieves the track entries from a CD's table of contents (TOC). The
+ * function can return up to 99 track entries, which is the maximum number of
+ * audio tracks the CD standard supports.
+ *
+ * This function only retrieve the minutes and seconds of an audio track's
+ * position as the CD-ROM controller only returns the minutes and seconds of a
+ * track, which may result in the end of the previous track being played
+ * instead of the intended track to be played. This can be remedied by having a
+ * 2 second pregap on each audio track on your disc.
+ *
+ * @param toc Pointer to an array of CdlLOC entries
+ * @return Number of tracks on the disc, zero on error.
+ *
+ * @see CdControl()
+ */
int CdGetToc(CdlLOC *toc);
+/**
+ * @brief Issues a control command to the CD-ROM controller
+ *
+ * @details Sends a CD-ROM command specified by com to the CD-ROM controller,
+ * waits for an acknowledge interrupt (very fast) then returns. It will also
+ * issue parameters from param to the CD-ROM controller if the command accepts
+ * parameters. Response data from the CD-ROM controller is stored to result on
+ * commands that produce response data.
+ *
+ * Because this function waits for an acknowledge interrupt from the CD-ROM
+ * controller, this function should not be used in a callback. Instead, use
+ * CdControlF().
+ *
+ * Commands that are blocking require the use of CdSync() to wait for the
+ * command to fully complete.
+ *
+ * CD-ROM control commands:
+ *
+ * | Command | Value | Parameter | Blocking | Description |
+ * | :------------ | ----: | :-------- | :------- | :----------------------------------------------------------------------------------------------------------------- |
+ * | CdlNop | 0x01 | | No | Also known as Getstat. Normally used to query the CD-ROM status, which is retrieved using CdStatus(). |
+ * | CdlSetloc | 0x02 | CdlLOC | No | Sets the seek target location, but does not seek. Actual seeking begins upon issuing a seek or read command. |
+ * | CdlPlay | 0x03 | uint8_t | No | Begins CD Audio playback. Parameter specifies an optional track number to play (some emulators do not support it). |
+ * | CdlForward | 0x04 | | No | Fast forward (CD Audio only), issue CdlPlay to stop fast forward. |
+ * | CdlBackward | 0x05 | | No | Rewind (CD Audio only), issue CdlPlay to stop rewind. |
+ * | CdlReadN | 0x06 | CdlLOC | No | Begin reading data sectors. Used in conjunction with CdReadCallback(). |
+ * | CdlStandby | 0x07 | | Yes | Also known as MotorOn, starts CD motor and remains idle. |
+ * | CdlStop | 0x08 | | Yes | Stops playback and the disc itself. |
+ * | CdlPause | 0x09 | | Yes | Stops playback or data reading, but leaves the disc on standby. |
+ * | CdlInit | 0x0A | | Yes | Initialize the CD-ROM controller. |
+ * | CdlMute | 0x0B | | No | Mutes CD audio (both DA and XA). |
+ * | CdlDemute | 0x0C | | No | Unmutes CD audio (both DA and XA). |
+ * | CdlSetfilter | 0x0D | CdlFILTER | No | Set XA audio filter. |
+ * | CdlSetmode | 0x0E | uint8_t | No | Set CD-ROM mode. |
+ * | CdlGetparam | 0x0F | | No | Returns current CD-ROM mode and file/channel filter settings. |
+ * | CdlGetlocL | 0x10 | | No | Returns current logical CD position, mode and XA filter parameters. |
+ * | CdlGetlocP | 0x11 | | No | Returns current physical CD position (using SubQ location data). |
+ * | CdlSetsession | 0x12 | uint8_t | Yes | Seek to specified session on a multi-session disc. |
+ * | CdlGetTN | 0x13 | | No | Get CD-ROM track count. |
+ * | CdlGetTD | 0x14 | uint8_t | No | Get specified track position. |
+ * | CdlSeekL | 0x15 | | Yes | Logical seek to target position, set by last CdlSetloc command. |
+ * | CdlSeekP | 0x16 | | Yes | Physical seek to target position, set by last CdlSetloc command. |
+ * | CdlTest | 0x19 | (varies) | Yes | Special test command not disclosed to official developers (see nocash documents for more info). |
+ * | CdlReadS | 0x1B | CdlLOC | No | Begin reading data sectors without pausing for error correction. |
+ *
+ * CD-ROM return values:
+ *
+ * | Command | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+ * | :---------- | :---- | :---- | :----- | :--- | :------ | :------ | :--- | :----- |
+ * | CdlGetparam | stat | mode | 0 | file | channel | | | |
+ * | CdlGetlocL | amin | asec | aframe | mode | file | channel | sm | ci |
+ * | CdlGetlocP | track | index | min | sec | frame | amin | asec | aframe |
+ * | CdlGetTN | stat | first | last | | | | | |
+ * | CdlGetTD | stat | min | sec | | | | | |
+ *
+ * NOTE: Values are in BCD format.
+ *
+ * @param com Command value
+ * @param param Command parameters
+ * @param result Pointer of buffer to store result
+ * @return 1 if the command was issued successfully. Otherwise 0 if a
+ * previously issued command has not yet finished processing.
+ *
+ * @see CdSync(), CdControlF()
+ */
int CdControl(uint8_t com, const void *param, uint8_t *result);
+
+/**
+ * @brief Issues a CD-ROM command to the CD-ROM controller (blocking)
+ *
+ * @details This function works just like CdControl(), but blocks on blocking
+ * commands until said blocking command has completed.
+ *
+ * Because this function waits for an acknowledge interrupt from the CD-ROM
+ * controller, this function should not be used in a callback. Use CdControlF()
+ * instead.
+ *
+ * @param com Command value
+ * @param param Command parameters
+ * @param result Pointer of buffer to store result
+ * @return 1 if the command was issued successfully. Otherwise 0 if a
+ * previously issued command has not yet finished processing.
+ *
+ * @see CdControl(), CdControlF()
+ */
int CdControlB(uint8_t com, const void *param, uint8_t *result);
+
+/**
+ * @brief Issues a CD-ROM command to the CD-ROM controller (does not block)
+ *
+ * @details This function works more or less the same as CdControl() but it
+ * does not block even for the acknowledge interrupt from the CD-ROM
+ * controller. Since this function is non-blocking it can be used in a callback
+ * function.
+ *
+ * When using this function in a callback, a maximum of two commands can be
+ * issued at once and only the first command can have parameters. This is
+ * because the CD-ROM controller can only queue up to two commands and the
+ * parameter FIFO is not cleared until the last command is acknowledged. But
+ * waiting for acknowledgment in a callback is not possible.
+ *
+ * @param com Command value
+ * @param param Command parameters
+ * @return 1 if the command was issued successfully. Otherwise 0 if a
+ * previously issued command has not yet finished processing.
+ *
+ * @see CdControl()
+ */
int CdControlF(uint8_t com, const void *param);
+
+/**
+ * @brief Waits for blocking command or blocking status
+ *
+ * @details If mode is zero the function blocks if a blocking command was
+ * issued earlier until the command has finished. If mode is non-zero the
+ * function returns a command status value.
+ *
+ * A buffer specified by result will be set with the most recent CD-ROM status
+ * value from the last command issued.
+ *
+ * @param mode Mode
+ * @param result Pointer to store most recent CD-ROM status
+ * @return Command status is returned as one of the following definitions:
+ *
+ * | Definition | Description |
+ * | :----------- | :-------------------------- |
+ * | CdlComplete | Command completed. |
+ * | CdlNoIntr | No interrupt, command busy. |
+ * | CdlDiskError | CD-ROM error occurred. |
+ *
+ * @see CdControl()
+ */
int CdSync(int mode, uint8_t *result);
+
+/**
+ * @brief Sets a callback function
+ *
+ * @details Sets a callback with the specified function func. The callback is
+ * executed whenever a blocking command has completed.
+ *
+ * status is the CD-ROM status from the command that has completed processing.
+ * *result corresponds to the *result parameter on CdControl()/CdControlB() and
+ * returns the pointer to the buffer last set with that function.
+ *
+ * @param func Callback function
+ * @return Pointer to last callback function set, or NULL if none was set.
+ *
+ * @see CdControl, CdControlB, CdSync
+ */
uint32_t CdSyncCallback(CdlCB func);
+/**
+ * @brief Sets a callback function
+ *
+ * @details Sets a callback with the specified function func. The callback is
+ * executed whenever there's an incoming data sector from the CD-ROM controller
+ * during CdlReadN or CdlReadS. The pending sector data can be retrieved using
+ * CdGetSector().
+ *
+ * status is the CD-ROM status code from the last CD command that has finished
+ * processing. *result corresponds to the result pointer that was passed by the
+ * last CdControl()/CdControlB() call.
+ *
+ * This callback cannot be used in conjunction with CdRead() because it also
+ * uses this callback hook for its own internal use. The previously set
+ * callback is restored after read completion however.
+ *
+ * @param func Callback function
+ * @return Pointer to last callback function set, or NULL if none was set.
+ *
+ * @see CdControl(), CdControlB(), CdGetSector()
+ */
int CdReadyCallback(CdlCB func);
+
+/**
+ * @brief Gets data from the CD-ROM sector buffered
+ *
+ * @details Reads sector data that is pending in the CD-ROM sector buffer and
+ * stores it to *madr. Uses DMA to transfer the sector data and blocks very
+ * briefly until said transfer completes.
+ *
+ * This function is intended to be called within a callback routine set using
+ * CdReadyCallback() to fetch read data sectors from the CD-ROM sector buffer.
+ *
+ * @param madr Pointer to memory buffer to store sector data
+ * @param size Number of 32-bit words to retrieve
+ * @return Always 1.
+ *
+ * @see CdReadyCallback()
+ */
int CdGetSector(void *madr, int size);
+
+/**
+ * @brief Gets data from the CD-ROM sector buffered (non-blocking)
+ *
+ * @details Reads sector data that is pending in the CD-ROM sector buffer and
+ * stores it to *madr. Uses DMA to transfer the sector data in the background
+ * while keeping the CPU running (one word is transferred every 16 CPU cycles).
+ * Note this is much slower than the blocking transfer performed by
+ * CdGetSector().
+ *
+ * This function is intended to be called within a callback routine set using
+ * CdReadyCallback() to fetch read data sectors from the CD-ROM sector buffer.
+ * Since the transfer is asynchronous, CdDataSync() should be used to wait
+ * until the whole sector has been read.
+ *
+ * @param madr Pointer to memory buffer to store sector data
+ * @param size Number of 32-bit words to retrieve
+ * @return Always 1.
+ *
+ * @see CdReadyCallback(), CdDataSync()
+ */
int CdGetSector2(void *madr, int size);
+
+/**
+ * @brief Waits for sector transfer to finish
+ *
+ * @details If mode is zero the function blocks until any sector DMA transfer
+ * initiated by calling CdGetSector2() has finished. If mode is non-zero the
+ * function returns a boolean value representing whether a transfer is
+ * currently in progress.
+ *
+ * @param mode Mode
+ * @return 0 if the transfer has finished, 1 if it is still in progress or -1
+ * in case of a timeout.
+ *
+ * @see CdGetSector2()
+ */
int CdDataSync(int mode);
+/**
+ * @brief Locates a file in the CD-ROM file system
+ *
+ * @details Searches a file specified by filename by path and name in the
+ * CD-ROM file system and returns information of the file if found. The file
+ * information acquired will be stored to loc.
+ *
+ * Directories can be separated with slashes (/) or backslashes (\), a leading
+ * slash or backslash is optional but paths must be absolute. File version
+ * identifier (;1) at the end of the file name is also optional. File and
+ * directory names are case insensitive.
+ *
+ * The ISO9660 file system routines of libpsxcd do not support long file names
+ * currently. Only MS-DOS style 8.3 file names are supported; extensions such
+ * as Joliet and Rock Ridge are ignored.
+ *
+ * Upon calling this function for the first time, the ISO descriptor of the
+ * disc is read and the whole path table is cached into memory. Next the
+ * directory descriptor of the particular directory specified is loaded and
+ * cached to locate the file specified. The directory descriptor is kept in
+ * memory as long as the consecutive files to be searched are stored in the
+ * same directory until a file in another directory is to be searched. On which
+ * the directory descriptor is unloaded and a new directory descriptor is read
+ * from the disc and cached. Therefore, locating files in the same directory is
+ * faster as the relevant directory descriptor is already in memory and no disc
+ * reads are issued.
+ *
+ * As of Revision 66 of PSn00bSDK, media change is detected by checking the
+ * CD-ROM lid open status bit and attempting to acknowledge it with a CdlNop
+ * command, to discriminate the status from an open lid or changed disc.
+ *
+ * @param loc Pointer to a CdlFILE struct to store file information
+ * @param filename Path and name of file to locate
+ * @return Pointer to the specified CdlFILE struct. Otherwise NULL is returned
+ * when the file is not found.
+ */
CdlFILE* CdSearchFile(CdlFILE *loc, const char *filename);
+/**
+ * @brief Reads sectors from the CD-ROM
+ *
+ * @details Reads a number sectors specified by sectors from the location set
+ * by the last CdlSetloc command, the read sectors are then stored to a buffer
+ * specified by buf. mode specifies the CD-ROM mode to use for the read
+ * operation.
+ *
+ * The size of the sector varies depending on the sector read mode specified by
+ * mode. For standard data sectors it is multiples of 2048 bytes. If
+ * CdlModeSize0 is specified the sector size is 2328 bytes which includes the
+ * whole sector minus sync, adress, mode and sub header bytes. CdlModeSize1
+ * makes the sector size 2340 which is the entire sector minus sync bytes.
+ * Ideally, CdlModeSpeed must be specified to read data sectors at double
+ * CD-ROM speed.
+ *
+ * This function blocks very briefly to issue the necessary commands to start
+ * CD-ROM reading. To determine if reading has completed use CdReadSync or
+ * CdReadCallback.
+ *
+ * @param sectors Number of sectors to read
+ * @param buf Pointer to buffer to store sectors read
+ * @param mode CD-ROM mode for reading
+ * @return Always returns 0 even on errors. This may change in future versions.
+ *
+ * @see CdReadSync(), CdReadCallback()
+ */
int CdRead(int sectors, uint32_t *buf, int mode);
+
+/**
+ * @brief Waits for CD-ROM read completion or returns read status
+ *
+ * @details This function works more or less like CdSync() but for CdRead(). If
+ * mode is zero the function blocks if CdRead() was issued earlier until
+ * reading has completed. If mode is non-zero the function completes
+ * immediately and returns number of sectors remaining.
+ *
+ * A buffer specified by result will be set with the most recent CD-ROM status
+ * value from the last read issued.
+ *
+ * @param mode Mode
+ * @param result Pointer to store most recent CD-ROM status
+ * @return Number of sectors remaining. If reading is completed, 0 is returned.
+ * On error, -1 is returned.
+ *
+ * @see CdRead()
+ */
int CdReadSync(int mode, uint8_t *result);
+
+/**
+ * @brief Sets a callback function for read completion
+ *
+ * @details Works much the same as CdSyncCallback() but for CdRead(). Sets a
+ * callback with the specified function func. The callback is executed whenever
+ * a read operation initiated by CdRead() has completed.
+ *
+ * status is the CD-ROM status from the command that has completed processing.
+ * *result points to a read result buffer.
+ *
+ * @param func Callback function
+ * @return Pointer to last callback function set, or NULL if none was set.
+ *
+ * @see CdRead()
+ */
uint32_t CdReadCallback(CdlCB func);
+/**
+ * @brief Gets the most recent CD-ROM status
+ *
+ * @details Returns the CD-ROM status since the last command issued. The status
+ * value is updated by most CD-ROM commands.
+ *
+ * To get the current CD-ROM status you can issue CdlNop commands at regular
+ * intervals to update the CD-ROM status this function returns.
+ *
+ * @return CD-ROM status from last comand issued.
+ *
+ * @see CdControl()
+ */
int CdStatus(void);
+
+/**
+ * @brief Gets the last CD-ROM mode
+ *
+ * @details Returns the CD-ROM mode last set when issuing a CdlSetmode command.
+ * The function returns instantly as it merely returns a value stored in an
+ * internal variable.
+ *
+ * Since the value is simply a copy of what was specified from the last
+ * CdlSetmode command, the mode value may become inaccurate if CdlInit or other
+ * commands that affect the CD-ROM mode have been issued previously.
+ *
+ * @return Last CD-ROM mode value.
+ */
int CdMode(void);
-int CdMix(CdlATV *vol);
+/**
+ * @brief Sets CD-ROM mixer or attenuation
+ *
+ * @details Sets the CD-ROM attenuation parameters from a CdlATV struct
+ * specified by vol. The CD-ROM attenuation settings are different from the SPU
+ * CD-ROM volume.
+ *
+ * Normally used to configure CD and XA audio playback for mono or reverse
+ * stereo output, though this was rarely used in practice.
+ *
+ * @param vol CD-ROM attenuation parameters
+ * @return Always 1.
+ */
+int CdMix(const CdlATV *vol);
-/* ORIGINAL CODE */
+/**
+ * @brief Opens a directory on the CD-ROM file system
+ *
+ * @details Opens a directory on the CD-ROM file system to read the contents of
+ * a directory.
+ *
+ * A path name can use a slash (/) or backslash character (\) as the directory
+ * name separator. The path must be absolute and should begin with a slash or
+ * backslash. It should also not be prefixed with a device name (ie.
+ * \MYDIR1\MYDIR2 will work but not cdrom:\MYDIR1\MYDIR2). The file system
+ * routines in libpsxcd can query directory paths of up to 128 characters.
+ *
+ * The ISO9660 file system routines of libpsxcd do not support long file names
+ * currently. Only MS-DOS style 8.3 file names are supported; extensions such
+ * as Joliet and Rock Ridge are ignored.
+ *
+ * @param path Directory path to open
+ * @return Pointer of a CdlDIR context, NULL if an error occurred.
+ *
+ * @see CdReadDir(), CdCloseDir()
+ */
CdlDIR* CdOpenDir(const char* path);
+
+/**
+ * @brief Reads a directory entry from an open directory context
+ *
+ * @details Retrieves a file entry from an open directory context and stores it
+ * to a CdlFILE struct specified by file. Repeated calls of this function
+ * retrieves the next directory entry available until there are no more
+ * directory entries that follow.
+ *
+ * @param dir Open directory context (from CdOpenDir())
+ * @param file Pointer to a CdlFILE struct
+ * @return 1 if there are proceeding directory entries that follow, otherwise 0.
+ *
+ * @see CdOpenDir()
+ */
int CdReadDir(CdlDIR* dir, CdlFILE* file);
+
+/**
+ * @brief Closes a directory context created by CdOpenDir()
+ *
+ * @details Closes a directory query context created by CdOpenDir(). Behavior
+ * is undefined when closing a previously closed directory context.
+ *
+ * @param dir Directory context
+ *
+ * @see CdOpenDir()
+ */
void CdCloseDir(CdlDIR* dir);
int CdGetVolumeLabel(char* label);
+/**
+ * @brief Sets a callback function for auto pause
+ *
+ * @details The callback function specified in *func is executed when an auto
+ * pause interrupt occurs when the current CD-ROM mode is set with CdlModeAP.
+ * Auto pause interrupt occurs when CD Audio playback reaches the end of the
+ * audio track. Specifying 0 disables the callback.
+ *
+ * This can be used to easily loop CD audio automatically without requiring any
+ * intervention in your software loop.
+ *
+ * @param func Callback function
+ * @return Pointer to the last callback function set. Zero if no callback was
+ * set previously.
+ *
+ * @see CdControl()
+ */
int* CdAutoPauseCallback(void(*func)());
-int CdIsoError();
+/**
+ * @brief Retrieves CD-ROM ISO9660 parser status
+ *
+ * @details Returns the status of the file system parser from the last call of
+ * a file system related function, such as CdSearchFile(), CdGetVolumeLabel()
+ * and CdOpenDir(). Use this function to retrieve the exact error occurred when
+ * either of those functions fail.
+ *
+ * @return CD-ROM ISO9660 parser error code, as listed below:
+ *
+ * | Value | Description |
+ * | :-------------- | :-------------------------------------------------------------------------------------------------- |
+ * | CdlIsoOkay | File system parser okay. |
+ * | CdlIsoSeekError | Logical seek error occurred. May occur when attempting to query the filesystem on an audio-only CD. |
+ * | CdlIsoReadError | Read error occurred while reading the CD-ROM file system descriptor. |
+ * | CdlIsoInvalidFs | Disc does not contain a standard ISO9660 file system. |
+ * | CdlIsoLidOpen | Lid is open when attempting to parse the CD-ROM file system. |
+ */
+int CdIsoError(void);
+
+/**
+ * @brief Locates and parses the specified disc session
+ *
+ * @details Loads a session specified by session on a multi-session disc. Uses
+ * CdlSetsession to seek to the specified disc session, then scans the
+ * following 512 sectors for an ISO volume descriptor. If a volume descriptor
+ * is found the file system of that session is parsed and files inside the new
+ * session can be accessed using regular CD-ROM file and directory querying
+ * functions (CdSearchFile(), CdOpenDir(), CdReadDir(), CdCloseDir()). No
+ * special consideration is required when reading files from a new session.
+ *
+ * Loading a session takes 5-10 seconds to complete depending on the distance
+ * between the beginning of the disc and the start of the specified session. If
+ * the session specified does not exist, the disc will stop and would take
+ * 15-20 seconds to restart. The function does not support loading the most
+ * recent session of a disc automatically due to limitations of the CD-ROM
+ * hardware, so the user must be prompted to specify which session to load and
+ * to keep a record of the number of sessions that have been written to the
+ * disc.
+ *
+ * This function can also be used to update the Table of Contents (TOC) and
+ * reparse the file system regardless of the media change status by simply
+ * loading the first session. This is most useful for accessing files or audio
+ * tracks on a disc that was inserted using the swap trick method (it is
+ * recommended to stop the disc using CdlStop then restart it with CdlStandby
+ * after a button prompt for convenience, if you wish to implement this
+ * capability). Seeking to sessions other than the first session does not work
+ * with the swap trick however, so a chipped or unlockable console is desired
+ * for reading multi-session discs.
+ *
+ * NOTE: When the lid has been opened, the current CD-ROM session is reset to
+ * the first session on the disc. The console may produce an audible click
+ * sound when executing this function. This is normal, and the click sound is
+ * no different to the click heard on disc spin-up in older models of the
+ * console.
+ *
+ * @param session Session number (1 = first session)
+ * @return 0 on success. On failure due to open lid, bad session number or no
+ * volume descriptor found in specified session, returns -1 and return value of
+ * CdIsoError() is updated.
+ */
int CdLoadSession(int session);
#ifdef __cplusplus
diff --git a/libpsn00b/include/psxetc.h b/libpsn00b/include/psxetc.h
index fcfec06..ae4611e 100644
--- a/libpsn00b/include/psxetc.h
+++ b/libpsn00b/include/psxetc.h
@@ -3,20 +3,45 @@
* (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed
*/
+/**
+ * @file psxetc.h
+ * @brief Interrupt management library header
+ *
+ * @details This library provides basic facilities (such as interrupt handling)
+ * used by all other PSn00bSDK libraries, as well as some additional
+ * functionality including a dynamic linker (whose API is however defined in a
+ * separate header).
+ */
+
#ifndef __PSXETC_H
#define __PSXETC_H
-/* Macros */
+/* IRQ and DMA channel definitions */
-// This macro is used internally by PSn00bSDK to log debug messages to a buffer
-// which is then printed to stdout when calling VSync().
-#ifdef NDEBUG
-#define _sdk_log(...)
-#define _sdk_dump_log()
-#else
-#define _sdk_log(...) _sdk_log_inner(__VA_ARGS__)
-#define _sdk_dump_log() _sdk_dump_log_inner()
-#endif
+typedef enum _IRQ_Channel {
+ IRQ_VBLANK = 0,
+ IRQ_GPU = 1,
+ IRQ_CD = 2,
+ IRQ_DMA = 3,
+ IRQ_TIMER0 = 4,
+ IRQ_TIMER1 = 5,
+ IRQ_TIMER2 = 6,
+ IRQ_SIO0 = 7,
+ IRQ_SIO1 = 8,
+ IRQ_SPU = 9,
+ IRQ_GUN = 10,
+ IRQ_PIO = 10
+} IRQ_Channel;
+
+typedef enum _DMA_Channel {
+ DMA_MDEC_IN = 0,
+ DMA_MDEC_OUT = 1,
+ DMA_GPU = 2,
+ DMA_CD = 3,
+ DMA_SPU = 4,
+ DMA_PIO = 5,
+ DMA_OTC = 6
+} DMA_Channel;
/* Public API */
@@ -24,16 +49,155 @@
extern "C" {
#endif
-void _sdk_log_inner(const char *fmt, ...);
-void _sdk_dump_log_inner(void);
+/**
+ * @brief Sets a callback for an interrupt.
+ *
+ * @details Registers a function to be called whenever the specified interrupt
+ * is fired. A previously registered callback can be removed by passing a null
+ * pointer instead. The IRQ controller is automatically configured to only
+ * enable interrupts for which a callback is registered.
+ *
+ * The callback will run in the exception handler's context, so it should be as
+ * fast as possible and shall not call any function that relies on interrupts
+ * being enabled. Each interrupt is acknowledged automatically before the
+ * callback is invoked.
+ *
+ * The following interrupt channels are available (the ones already used
+ * internally by libraries shall not be overridden to avoid conflicts):
+ *
+ * | ID | Channel | Used by |
+ * | --: | :--------------- | :-------------------------------------- |
+ * | 0 | IRQ_VBLANK | psxgpu (use VSyncCallback() instead) |
+ * | 1 | IRQ_GPU | |
+ * | 2 | IRQ_CD | psxcd (use CdReadyCallback() instead) |
+ * | 3 | IRQ_DMA | psxetc (use DMACallback() instead) |
+ * | 4 | IRQ_TIMER0 | |
+ * | 5 | IRQ_TIMER1 | |
+ * | 6 | IRQ_TIMER2 | |
+ * | 7 | IRQ_SIO0 | |
+ * | 8 | IRQ_SIO1 | psxsio (use SIO_ReadCallback() instead) |
+ * | 9 | IRQ_SPU | |
+ * | 10 | IRQ_GUN, IRQ_PIO | |
+ *
+ * WARNING: even though interrupts are acknowledged automatically at the IRQ
+ * controller side, most IRQ channels (1, 2, 3, 7, 8, 9) additionally require
+ * acknowledging at the device side, which must be done by the callback. The
+ * exact way to acknowledge interrupts varies for each device, however it
+ * usually involves setting or clearing a bit in a register. See the nocash
+ * documentation for more details.
+ *
+ * @param irq
+ * @param func
+ * @return Previously set callback for the channel or NULL
+ */
+void *InterruptCallback(IRQ_Channel irq, void (*func)(void));
+
+/**
+ * @brief Gets the callback for an interrupt.
+ *
+ * @details Returns a pointer to the callback currently registered to handle
+ * the specified interrupt, or a null pointer if none is set.
+ *
+ * @param irq
+ * @return Currently set callback for the channel or NULL
+ *
+ * @see InterruptCallback()
+ */
+void *GetInterruptCallback(IRQ_Channel irq);
-void *InterruptCallback(int irq, void (*func)(void));
-void *GetInterruptCallback(int irq);
-void *DMACallback(int dma, void (*func)(void));
-void *GetDMACallback(int dma);
+/**
+ * @brief Sets a callback for a DMA interrupt.
+ *
+ * @details Registers a function to be called whenever the specified DMA
+ * channel goes from busy to idle, i.e. when a transfer is completed. A
+ * previously registered callback can be removed by passing a null pointer
+ * instead. The DMA controller is automatically configured to only enable DMA
+ * interrupts for which a callback is registered.
+ *
+ * This function uses InterruptCallback() to register a "master handler" for
+ * DMA interrupts, which then dispatches the IRQ to depending on the channel
+ * that triggered it.
+ *
+ * The callback will run in the exception handler's context, so it should be as
+ * fast as possible and shall not call any function that relies on interrupts
+ * being enabled. Each interrupt is acknowledged automatically before the
+ * callback is invoked.
+ *
+ * The following DMA channels are available (the ones already used internally
+ * by libraries shall not be overridden to avoid conflicts):
+ *
+ * | ID | Channel | Used by |
+ * | --: | :----------- | :-------------------------------------- |
+ * | 0 | DMA_MDEC_IN | |
+ * | 1 | DMA_MDEC_OUT | |
+ * | 2 | DMA_GPU | psxgpu (use DrawSyncCallback() instead) |
+ * | 3 | DMA_CD | |
+ * | 4 | DMA_SPU | |
+ * | 5 | DMA_PIO | |
+ * | 6 | DMA_OTC | |
+ *
+ * @param dma
+ * @param func
+ * @return Previously set callback for the channel or NULL
+ */
+void *DMACallback(DMA_Channel dma, void (*func)(void));
+/**
+ * @brief Gets the callback for a DMA interrupt.
+ *
+ * @details Returns a pointer to the callback currently registered to handle
+ * the specified DMA interrupt, or a null pointer if none is set.
+ *
+ * @param dma
+ * @return Currently set callback for the channel or NULL
+ *
+ * @see DMACallback()
+ */
+void *GetDMACallback(DMA_Channel dma);
+
+/**
+ * @brief Initializes the interrupt dispatcher.
+ *
+ * @details Sets up the interrupt handling system, hooks the BIOS to dispatch
+ * interrupts to the library and clears all registered callbacks. This function
+ * must be called once at the beginning of the program, prior to registering
+ * any IRQ or DMA callbacks.
+ *
+ * ResetCallback() is called by psxgpu's ResetGraph(), so invoking it manually
+ * is usually not required. Calling ResetCallback() after ResetGraph() will
+ * actually result in improper initialization, as ResetGraph() registers
+ * several callbacks used internally by psxgpu.
+ *
+ * @return 0 or -1 if the was already initialized
+ */
int ResetCallback(void);
+
+/**
+ * @brief Restores the interrupt dispatcher.
+ *
+ * @details Restores the IRQ and DMA controller state saved by StopCallback()
+ * and reinstalls BIOS hooks for interrupt dispatching. All callbacks
+ * previously set before StopCallback() was called are preserved.
+ *
+ * @see StopCallback()
+ */
void RestartCallback(void);
+
+/**
+ * @brief Temporarily disables the interrupt dispatcher.
+ *
+ * @details Saves the state of the IRQ and DMA controllers, then disables them
+ * and removes BIOS hooks. This function must be called prior to launching a
+ * new executable or DLL that calls ResetCallback() or ResetGraph(), or an
+ * executable not built with PSn00bSDK that uses its own interrupt handling
+ * subsystem (such as a retail game). The saved state can be restored after the
+ * executable returns using RestartCallback().
+ *
+ * Note that interrupts are (obviously) disabled until RestartCallback() is
+ * called.
+ *
+ * @see RestartCallback()
+ */
void StopCallback(void);
#ifdef __cplusplus
diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h
index f2568b0..68e3bff 100644
--- a/libpsn00b/include/psxgpu.h
+++ b/libpsn00b/include/psxgpu.h
@@ -31,65 +31,65 @@ typedef enum _GPU_VideoMode {
/* Structure macros */
#define setVector(v, _x, _y, _z) \
- (v)->vx = _x, (v)->vy = _y, (v)->vz = _z
+ (v)->vx = (_x), (v)->vy = (_y), (v)->vz = (_z)
#define setRECT(v, _x, _y, _w, _h) \
- (v)->x = _x, (v)->y = _y, (v)->w = _w, (v)->h = _h
+ (v)->x = (_x), (v)->y = (_y), (v)->w = (_w), (v)->h = (_h)
#define setTPage(p, tp, abr, x, y) ((p)->tpage = getTPage(tp, abr, x, y))
#define setClut(p, x, y) ((p)->clut = getClut(x, y))
-#define setRGB0(p, r, g, b) ((p)->r0 = r, (p)->g0 = g, (p)->b0 = b)
-#define setRGB1(p, r, g, b) ((p)->r1 = r, (p)->g1 = g, (p)->b1 = b)
-#define setRGB2(p, r, g, b) ((p)->r2 = r, (p)->g2 = g, (p)->b2 = b)
-#define setRGB3(p, r, g, b) ((p)->r3 = r, (p)->g3 = g, (p)->b3 = b)
+#define setRGB0(p, r, g, b) ((p)->r0 = (r), (p)->g0 = (g), (p)->b0 = (b))
+#define setRGB1(p, r, g, b) ((p)->r1 = (r), (p)->g1 = (g), (p)->b1 = (b))
+#define setRGB2(p, r, g, b) ((p)->r2 = (r), (p)->g2 = (g), (p)->b2 = (b))
+#define setRGB3(p, r, g, b) ((p)->r3 = (r), (p)->g3 = (g), (p)->b3 = (b))
#define setXY0(p, _x0, _y0) \
- (p)->x0 = _x0, (p)->y0 = _y0
+ (p)->x0 = (_x0), (p)->y0 = (_y0)
#define setXY2(p, _x0, _y0, _x1, _y1) \
- (p)->x0 = _x0, (p)->y0 = _y0, \
- (p)->x1 = _x1, (p)->y1 = _y1
+ (p)->x0 = (_x0), (p)->y0 = (_y0), \
+ (p)->x1 = (_x1), (p)->y1 = (_y1)
#define setXY3(p, _x0, _y0, _x1, _y1, _x2, _y2) \
- (p)->x0 = _x0, (p)->y0 = _y0, \
- (p)->x1 = _x1, (p)->y1 = _y1, \
- (p)->x2 = _x2, (p)->y2 = _y2
+ (p)->x0 = (_x0), (p)->y0 = (_y0), \
+ (p)->x1 = (_x1), (p)->y1 = (_y1), \
+ (p)->x2 = (_x2), (p)->y2 = (_y2)
#define setXY4(p, _x0, _y0, _x1, _y1, _x2, _y2, _x3, _y3) \
- (p)->x0 = _x0, (p)->y0 = _y0, \
- (p)->x1 = _x1, (p)->y1 = _y1, \
- (p)->x2 = _x2, (p)->y2 = _y2, \
- (p)->x3 = _x3, (p)->y3 = _y3
+ (p)->x0 = (_x0), (p)->y0 = (_y0), \
+ (p)->x1 = (_x1), (p)->y1 = (_y1), \
+ (p)->x2 = (_x2), (p)->y2 = (_y2), \
+ (p)->x3 = (_x3), (p)->y3 = (_y3)
#define setWH(p, _w, _h) \
- (p)->w = _w, (p)->h = _h
+ (p)->w = (_w), (p)->h = (_h)
#define setXYWH(p, _x0, _y0, _w, _h) \
- (p)->x0 = _x0, (p)->y0 = _y0, \
- (p)->x1 = (_x0 + (_w)), (p)->y1 = _y0, \
- (p)->x2 = _x0, (p)->y2 = (_y0 + (_h)), \
- (p)->x3 = (_x0 + (_w)), (p)->y3 = (_y0 + (_h))
+ (p)->x0 = (_x0), (p)->y0 = (_y0), \
+ (p)->x1 = ((_x0) + (_w)), (p)->y1 = (_y0), \
+ (p)->x2 = (_x0), (p)->y2 = ((_y0) + (_h)), \
+ (p)->x3 = ((_x0) + (_w)), (p)->y3 = ((_y0) + (_h))
#define setUV0(p, _u0, _v0) \
- (p)->u0 = _u0, (p)->v0 = _v0
+ (p)->u0 = (_u0), (p)->v0 = (_v0)
#define setUV3(p, _u0, _v0, _u1, _v1, _u2, _v2) \
- (p)->u0 = _u0, (p)->v0 = _v0, \
- (p)->u1 = _u1, (p)->v1 = _v1, \
- (p)->u2 = _u2, (p)->v2 = _v2
+ (p)->u0 = (_u0), (p)->v0 = (_v0), \
+ (p)->u1 = (_u1), (p)->v1 = (_v1), \
+ (p)->u2 = (_u2), (p)->v2 = (_v2)
#define setUV4(p, _u0, _v0, _u1, _v1, _u2, _v2, _u3, _v3) \
- (p)->u0 = _u0, (p)->v0 = _v0, \
- (p)->u1 = _u1, (p)->v1 = _v1, \
- (p)->u2 = _u2, (p)->v2 = _v2, \
- (p)->u3 = _u3, (p)->v3 = _v3
+ (p)->u0 = (_u0), (p)->v0 = (_v0), \
+ (p)->u1 = (_u1), (p)->v1 = (_v1), \
+ (p)->u2 = (_u2), (p)->v2 = (_v2), \
+ (p)->u3 = (_u3), (p)->v3 = (_v3)
#define setUVWH(p, _u0, _v0, _w, _h) \
- (p)->u0 = _u0, (p)->v0 = _v0, \
- (p)->u1 = (_u0 + (_w)), (p)->v1 = _v0, \
- (p)->u2 = _u0, (p)->v2 = (_v0 + (_h)), \
- (p)->u3 = (_u0 + (_w)), (p)->v3 = (_v0 + (_h))
+ (p)->u0 = (_u0), (p)->v0 = (_v0), \
+ (p)->u1 = ((_u0) + (_w)), (p)->v1 = (_v0), \
+ (p)->u2 = (_u0), (p)->v2 = ((_v0) + (_h)), \
+ (p)->u3 = ((_u0) + (_w)), (p)->v3 = ((_v0) + (_h))
/* Primitive handling macros */
@@ -113,8 +113,13 @@ typedef enum _GPU_VideoMode {
#define setShadeTex(p, tge) \
((tge) ? (getcode(p) |= 1) : (getcode(p) &= ~1))
-#define getTPage(tp, abr, x, y) \
- ((((x) & 0x3ff) >> 6) | (((y) >> 8) << 4) | (((abr) & 3) << 5) | (((tp) & 3) << 7))
+#define getTPage(tp, abr, x, y) ( \
+ (((x) / 64) & 15) | \
+ ((((y) / 256) & 1) << 4) | \
+ (((abr) & 3) << 5) | \
+ (((tp) & 3) << 7) | \
+ ((((y) / 512) & 1) << 11) \
+)
#define getClut(x, y) (((y) << 6) | (((x) >> 4) & 0x3f))
@@ -151,24 +156,42 @@ typedef enum _GPU_VideoMode {
#define setDrawTPage(p, dfe, dtd, tpage) \
setlen(p, 1), \
- (p)->code[0] = 0xe1000000 | tpage | (dfe << 10) | (dtd << 9)
+ (p)->code[0] = (0xe1000000 | \
+ (tpage) | \
+ ((dtd) << 9) | \
+ ((dfe) << 10) \
+ )
#define setDrawOffset(p, _x, _y) \
setlen(p, 1), \
- (p)->code[0] = 0xe5000000 | (_x & 0x3ff) | ((_y & 0x3ff) << 11)
+ (p)->code[0] = (0xe5000000 | \
+ ((_x) % 1024) | \
+ (((_y) % 1024) << 11) \
+ )
#define setDrawMask(p, sb, mt) \
setlen(p, 1), \
- (p)->code[0] = 0xe6000000 | sb | (mt << 1)
+ (p)->code[0] = (0xe6000000 | (sb) | ((mt) << 1))
#define setDrawArea(p, r) \
setlen(p, 2), \
- (p)->code[0] = 0xe3000000 | ((r)->x & 0x3ff) | (((r)->y & 0x1ff) << 10), \
- (p)->code[1] = 0xe4000000 | (((r)->x + (r)->w - 1) & 0x3ff) | ((((r)->y + (r)->h - 1) & 0x1ff) << 10)
+ (p)->code[0] = (0xe3000000 | \
+ ((r)->x % 1024) | \
+ (((r)->y % 1024) << 10) \
+ ), \
+ (p)->code[1] = (0xe4000000 | \
+ (((r)->x + (r)->w - 1) % 1024) | \
+ ((((r)->y + (r)->h - 1) % 1024) << 10) \
+ )
#define setTexWindow(p, r) \
setlen(p, 1), \
- (p)->code[0] = 0xe2000000 | ((r)->w & 0x1f) | (((r)->h & 0x1f) << 5) | (((r)->x & 0x1f) << 10) | (((r)->y & 0x1f) << 15)
+ (p)->code[0] = (0xe2000000 | \
+ ((r)->w % 32) | \
+ (((r)->h % 32) << 5) | \
+ (((r)->x % 32) << 10) | \
+ (((r)->y % 32) << 15) \
+ )
/* Primitive structure definitions */
diff --git a/libpsn00b/include/psxgte.h b/libpsn00b/include/psxgte.h
index ddc988d..3c1d5a4 100644
--- a/libpsn00b/include/psxgte.h
+++ b/libpsn00b/include/psxgte.h
@@ -3,6 +3,17 @@
* (C) 2019-2022 Lameguy64 - MPL licensed
*/
+/**
+ * @file psxgte.h
+ * @brief GTE library header
+ *
+ * @details The Geometry Transformation Engine, often referred to as the GTE,
+ * is most responsible for providing 3D capabilities to the PS1. This is
+ * effectively an all-integer math co-processor connected directly to the CPU,
+ * as it is accessed using COP2 and related MIPS instructions to access
+ * registers and issue commands to the GTE.
+ */
+
#ifndef __PSXGTE_H
#define __PSXGTE_H
@@ -35,50 +46,216 @@ typedef struct _DVECTOR {
/* Public API */
+#define csin(a) isin(a)
+#define ccos(a) icos(a)
+#define rsin(a) isin(a)
+#define rcos(a) icos(a)
+
#ifdef __cplusplus
extern "C" {
#endif
-void InitGeom(void);
-
-// Integer SIN/COS functions (4096 = 360 degrees)
-// Does not use tables!
+/**
+ * @brief Gets sine of angle (fixed-point, high precision version)
+ *
+ * @details Returns the sine of angle a.
+ *
+ * @param a Angle in fixed-point format (131072 = 360 degrees)
+ * @return Sine value in 20.12 fixed-point format (4096 = 1.0).
+ */
int isin(int a);
+
+/**
+ * @brief Gets cosine of angle (fixed-point, high precision version)
+ *
+ * @details Returns the cosine of angle a.
+ *
+ * @param a Angle in fixed-point format (131072 = 360 degrees)
+ * @return Cosine value in 20.12 fixed-point format (4096 = 1.0).
+ */
int icos(int a);
-// Higher precision integer sin/cos functions (131072 = 360 degrees)
-// Does not use tables!
+/**
+ * @brief Gets sine of angle (fixed-point)
+ *
+ * @details Returns the sine of angle a.
+ *
+ * @param a Angle in fixed-point format (131072 = 360 degrees)
+ * @return Sine value in 20.12 fixed-point format (4096 = 1.0).
+ */
int hisin(int a);
+
+/**
+ * @brief Gets cosine of angle (fixed-point)
+ *
+ * @details Returns the cosine of angle a.
+ *
+ * @param a Angle in fixed-point format (131072 = 360 degrees)
+ * @return Cosine value in 20.12 fixed-point format (4096 = 1.0).
+ */
int hicos(int a);
+/**
+ * @brief Initializes the GTE
+ *
+ * @details Resets, enables and initializes the GTE. Must be called prior to
+ * using any GTE function or macro.
+ */
+void InitGeom(void);
+
+/**
+ * @brief Gets square root (fixed-point)
+ *
+ * @details Returns the square root of value v.
+ *
+ * @param v Value in 20.12 fixed-point format (4096 = 1.0)
+ * @return Square root in 20.12 fixed-point format (4096 = 1.0).
+ */
+int SquareRoot12(int v);
+
+/**
+ * @brief Gets square root (integer)
+ *
+ * @details Returns the square root of value v.
+ *
+ * @param v Value in integer format
+ * @return Square root in integer format.
+ */
+int SquareRoot0(int v);
+
+/**
+ * @brief Pushes the current GTE matrix to the matrix stack
+ *
+ * @details Pushes the current GTE rotation matrix and translation vector to
+ * the internal matrix stack. Only one matrix stack level is currently
+ * supported.
+ */
void PushMatrix(void);
+
+/**
+ * @brief Pops the last matrix pushed into the matrix stack back to the GTE
+ *
+ * @details Pops the last inserted matrix in the internal matrix stack back to
+ * the GTE. Only one matrix stack level is currently supported.
+ */
void PopMatrix(void);
+/**
+ * @brief Defines the rotation matrix of a MATRIX
+ *
+ * @details Defines the rotation matrix of m from rotation coordinates of r.
+ * The matrix is computed as follows:
+ *
+ * [ 1 0 0 ] [ cy 0 sy] [ cz -sz 0 ]
+ * [ 0 cx -sx] * [ 0 1 0 ] * [ sz cz 0 ]
+ * [ 0 sx cx] [-sy 0 cy] [ 0 0 1 ]
+ *
+ * where:
+ *
+ * sx = sin(r.x) sy = sin(r.y) sz = sin(r.z)
+ * cx = cos(r.x) cy = cos(r.y) cz = cos(r.z)
+ *
+ * @param r Rotation vector (input)
+ * @param m Matrix (output)
+ * @return Pointer to m.
+ *
+ * @see TransMatrix(), CompMatrixLV()
+ */
MATRIX *RotMatrix(SVECTOR *r, MATRIX *m);
+
+/**
+ * @brief Defines the rotation matrix of a MATRIX (high precision version)
+ *
+ * @details Defines the rotation matrix of m from rotation coordinates of r.
+ * This function is a variant of RotMatrix() that uses hisin()/hicos() instead
+ * of isin()/icos().
+ *
+ * See RotMatrix() for more details.
+ *
+ * @param r Rotation vector (input)
+ * @param m Matrix (output)
+ * @return Pointer to m.
+ *
+ * @see RotMatrix()
+ */
MATRIX *HiRotMatrix(VECTOR *r, MATRIX *m);
+/**
+ * @brief Defines the translation vector of a MATRIX
+ *
+ * @details Simply sets the translation vector of MATRIX m. To perform
+ * accumulative translation operations, see CompMatrixLV().
+ *
+ * @param m Translation vector (input)
+ * @param r Matrix (output)
+ * @return Pointer to m.
+ *
+ * @see RotMatrix(), CompMatrixLV()
+ */
MATRIX *TransMatrix(MATRIX *m, VECTOR *r);
+
MATRIX *ScaleMatrix(MATRIX *m, VECTOR *s);
MATRIX *ScaleMatrixL(MATRIX *m, VECTOR *s);
MATRIX *MulMatrix(MATRIX *m0, MATRIX *m1);
MATRIX *MulMatrix0(MATRIX *m0, MATRIX *m1, MATRIX *m2);
+/**
+ * @brief Composite coordinate matrix transform
+ *
+ * @details Performs vector multiply by matrix with vector addition from v0 to
+ * the translation vector of v1. Then, multiples the rotation matrix of v0 by
+ * the rotation matrix of v1. The result of both operations is then stored in
+ * v2. Replaces the current GTE rotation matrix and translation vector with v0.
+ *
+ * Often used to adjust the matrix (includes rotation and translation) of an
+ * object relative to a world matrix, so the object would render relative to
+ * the world matrix.
+ *
+ * @param v0 Input matrix A
+ * @param v1 Input matrix B
+ * @param v2 Output matrix
+ * @return Pointer to v2.
+ */
MATRIX *CompMatrixLV(MATRIX *v0, MATRIX *v1, MATRIX *v2);
+
+/**
+ * @brief Multiplies a vector by a matrix
+ *
+ * @details Multiplies vector v0 with matrix m, result is stored to v1.
+ * Replaces the current GTE rotation matrix and translation vector with m.
+ *
+ * Often used to calculate a translation vector in relation to the rotation
+ * matrix for first person or vector camera perspectives.
+ *
+ * @param m Input matrix
+ * @param v0 Input vector
+ * @param v1 Output vector
+ * @return Pointer to v1.
+ */
VECTOR *ApplyMatrixLV(MATRIX *m, VECTOR *v0, VECTOR *v1);
+/**
+ * @brief Normalizes a VECTOR into SVECTOR format
+ *
+ * Normalizes a 32-bit vector into a 16-bit vector in 4.12 fixed-point format
+ * (4096 = 1.0, 2048 = 0.5).
+ *
+ * @param v0 Input (raw) 32-bit vector
+ * @param v1 Output (normalized) 16-bit vector
+ */
void VectorNormalS(VECTOR *v0, SVECTOR *v1);
+/**
+ * @brief Calculates the square of a VECTOR
+ *
+ * @details Calculates the square of vector v0 and stores the result to v1.
+ *
+ * @param v0 Input vector
+ * @param v1 Output vector
+ */
void Square0(VECTOR *v0, VECTOR *v1);
-int SquareRoot12(int v);
-int SquareRoot0(int v);
-
-#define csin(a) isin(a)
-#define ccos(a) icos(a)
-#define rsin(a) isin(a)
-#define rcos(a) icos(a)
-
#ifdef __cplusplus
}
#endif
diff --git a/libpsn00b/include/psxpress.h b/libpsn00b/include/psxpress.h
index 2106a53..dc1d52c 100644
--- a/libpsn00b/include/psxpress.h
+++ b/libpsn00b/include/psxpress.h
@@ -3,6 +3,23 @@
* (C) 2022 spicyjpeg - MPL licensed
*/
+/**
+ * @file psxpress.h
+ * @brief MDEC library header
+ *
+ * @details This is a fully original reimplementation of the official SDK's
+ * "data compression" library. This library is made up of two parts, the MDEC
+ * API and functions to decompress Huffman-encoded bitstreams (.BS files, or
+ * frames in .STR files) into data to be fed to the MDEC. Two different
+ * implementations of the latter are provided, one using the GTE and scratchpad
+ * region and an older one using a large lookup table in main RAM.
+ *
+ * FMV playback is not part of this library per se, but can implemented using
+ * the APIs defined here alongside some code to stream data from the CD drive.
+ *
+ * Currently only version 1 and 2 .BS files are supported.
+ */
+
#ifndef __PSXPRESS_H
#define __PSXPRESS_H
@@ -70,7 +87,9 @@ extern "C" {
#endif
/**
- * @brief Resets the MDEC and aborts any MDEC DMA transfers. If mode = 0, the
+ * @brief Resets and optionally initializes the MDEC.
+ *
+ * @details Resets the MDEC and aborts any MDEC DMA transfers. If mode = 0, the
* default IDCT matrix and quantization tables are also loaded and the MDEC is
* put into color output mode, discarding any custom environment previously set
* with DecDCTPutEnv().
@@ -78,15 +97,19 @@ extern "C" {
* DecDCTReset(0) must be called at least once prior to using the MDEC.
*
* @param mode
+ *
+ * @see DecDCTPutEnv()
*/
void DecDCTReset(int mode);
/**
- * @brief Uploads the specified decoding environment's quantization tables and
- * IDCT matrix to the MDEC, or restores the default tables if a null pointer is
- * passed. Calling this function is normally not required as DecDCTReset(0)
- * initializes the MDEC with the default tables, but it may be useful for e.g.
- * decoding JPEG or a format with custom quantization tables.
+ * @brief Loads default or custom quantization and IDCT tables into the MDEC.
+ *
+ * @details Uploads the specified decoding environment's quantization tables
+ * and IDCT matrix to the MDEC, or restores the default tables if a null
+ * pointer is passed. Calling this function is normally not required as
+ * DecDCTReset(0) initializes the MDEC with the default tables, but it may be
+ * useful for e.g. decoding JPEG or a format with custom quantization tables.
*
* The second argument, not present in the official SDK, specifies whether the
* MDEC shall be put into color (0) or monochrome (1) output mode. In
@@ -103,12 +126,16 @@ void DecDCTReset(int mode);
void DecDCTPutEnv(const DECDCTENV *env, int mono);
/**
- * @brief Sets up the MDEC to start fetching and decoding the given buffer.
- * This function is meant to be used with buffers generated by DecDCTvlc(): the
- * first 32-bit word of the buffer is initially copied to the MDEC0 register,
- * then all subsequent data is read in 128-byte (32-word) chunks. The length of
- * the stream (in 32-bit units, minus the first word) is encoded by DecDCTvlc()
- * in the lower 16 bits of the first word.
+ * @brief Feeds the MDEC with a run-length code buffer from the specified
+ * location.
+ *
+ * @details Sets up the MDEC to start fetching and decoding the given buffer.
+ * This function is meant to be used with buffers generated by DecDCTvlc(),
+ * DecDCTvlc2() or their variants: the first 32-bit word of the buffer is
+ * initially copied to the MDEC0 register, then all subsequent data is read in
+ * 128-byte (32-word) chunks. The length of the stream (in 32-bit units, minus
+ * the first word) is encoded by DecDCTvlc() in the lower 16 bits of the first
+ * word.
*
* The mode argument optionally specifies the output color depth (0 for 16bpp,
* 1 for 24bpp) if not already set in the first word. Passing -1 will result in
@@ -117,11 +144,15 @@ void DecDCTPutEnv(const DECDCTENV *env, int mono);
*
* @param data
* @param mode DECDCT_MODE_* or -1
+ *
+ * @see DecDCTinRaw(), DecDCTinSync()
*/
void DecDCTin(const uint32_t *data, int mode);
/**
- * @brief Configures the MDEC to automatically fetch data (the input stream,
+ * @brief Feeds the MDEC with raw data from the specified location.
+ *
+ * @details Configures the MDEC to automatically fetch data (the input stream,
* IDCT matrix or quantization tables) in 128-byte (32-word) chunks from the
* specified address in main RAM. The transfer is stopped, and any callback
* registered with DMACallback(0) is fired, once a certain number of 32-bit
@@ -135,13 +166,17 @@ void DecDCTin(const uint32_t *data, int mode);
*
* @param data
* @param length Number of 32-bit words to read (must be multiple of 32)
+ *
+ * @see DecDCTin(), DecDCTinSync()
*/
void DecDCTinRaw(const uint32_t *data, size_t length);
/**
- * @brief Waits for the MDEC to finish decoding the input stream (if mode = 0)
- * or returns whether it is busy (if mode = 1). MDEC commands can be issued
- * only when the MDEC isn't busy.
+ * @brief Waits for an MDEC input transfer to finish or returns its status.
+ *
+ * @details Waits for the MDEC to finish decoding the input stream (if
+ * mode = 0) or returns whether it is busy (if mode = 1). MDEC commands can be
+ * issued only when the MDEC isn't busy.
*
* WARNING: DecDCTinSync(0) might time out and return -1 if the MDEC can't
* output decoded data, e.g. if the length passed DecDCTout() was too small and
@@ -155,7 +190,9 @@ void DecDCTinRaw(const uint32_t *data, size_t length);
int DecDCTinSync(int mode);
/**
- * @brief Configures the MDEC to automatically transfer decoded image data in
+ * @brief Writes image data decoded by the MDEC to the specified location.
+ *
+ * @details Configures the MDEC to automatically transfer decoded image data in
* 128-byte (32-word) chunks to the specified address in main RAM. MDEC
* operation is paused once a certain number of 32-bit words have been output
* and can be resumed by calling DecDCTout() again: the MDEC will continue
@@ -168,12 +205,16 @@ int DecDCTinSync(int mode);
*
* @param data
* @param length Number of 32-bit words to output (must be multiple of 32)
+ *
+ * @see DecDCToutSync()
*/
void DecDCTout(uint32_t *data, size_t length);
/**
- * @brief Waits until the transfer set up by DecDCTout() finishes (if mode = 0)
- * or returns whether it is still in progress (if mode = 1).
+ * @brief Waits for an MDEC output transfer to finish or returns its status.
+ *
+ * @details Waits until the transfer set up by DecDCTout() finishes (if
+ * mode = 0) or returns whether it is still in progress (if mode = 1).
*
* WARNING: DecDCToutSync(0) might time out and return -1 if the MDEC is unable
* to consume enough input data in order to produce the desired amount of data.
@@ -186,7 +227,9 @@ void DecDCTout(uint32_t *data, size_t length);
int DecDCToutSync(int mode);
/**
- * @brief Begins decompressing the contents of a .BS file (or of a single .STR
+ * @brief Decompresses or begins decompressing a .BS file into MDEC codes.
+ *
+ * @details Begins decompressing the contents of a .BS file (or of a single STR
* frame) into a buffer that can be passed to DecDCTin(). This function uses a
* small (<1 KB) lookup table combined with the GTE to accelerate the process;
* performance is roughly on par with DecDCTvlcStart2() if the lookup table
@@ -212,11 +255,15 @@ int DecDCToutSync(int mode);
* @param max_size Maximum number of 32-bit words to output
* @param bs
* @return 0, 1 if more data needs to be output or -1 in case of failure
+ *
+ * @see DecDCTvlcContinue(), DecDCTvlcCopyTable()
*/
int DecDCTvlcStart(VLC_Context *ctx, uint32_t *buf, size_t max_size, const uint32_t *bs);
/**
- * @brief Resumes the decompression process started by DecDCTvlcStart(). The
+ * @brief Resumes or finishes decompressing a .BS file into MDEC codes.
+ *
+ * @details Resumes the decompression process started by DecDCTvlcStart(). The
* state of the decompressor is contained entirely in the VLC_Context structure
* so an arbitrary number of bitstreams can be decoded concurrently (although
* the limited CPU power makes it impractical to do so) by keeping a separate
@@ -236,14 +283,18 @@ int DecDCTvlcStart(VLC_Context *ctx, uint32_t *buf, size_t max_size, const uint3
* @param buf
* @param max_size Maximum number of 32-bit words to output
* @return 0, 1 if more data needs to be output or -1 in case of failure
+ *
+ * @see DecDCTvlcStart()
*/
int DecDCTvlcContinue(VLC_Context *ctx, uint32_t *buf, size_t max_size);
/**
- * A wrapper around DecDCTvlcStart() and DecDCTvlcContinue() for compatibility
- * with the official SDK. This function uses an internal context; additionally,
- * the maximum output buffer size is not passed as an argument but is instead
- * set by calling DecDCTvlcSize().
+ * @brief Decompresses a .BS file into MDEC codes.
+ *
+ * @details A wrapper around DecDCTvlcStart() and DecDCTvlcContinue() for
+ * compatibility with the official SDK. This function uses an internal context;
+ * additionally, the maximum output buffer size is not passed as an argument
+ * but is instead set by calling DecDCTvlcSize().
*
* This function behaves identically to DecDCTvlcContinue() if bs = 0 and
* DecDCTvlcStart() otherwise.
@@ -257,21 +308,34 @@ int DecDCTvlcContinue(VLC_Context *ctx, uint32_t *buf, size_t max_size);
* @param bs Pointer to bitstream data or 0 to resume decoding
* @param buf
* @return 0, 1 if more data needs to be output or -1 in case of failure
+ *
+ * @see DecDCTvlcSize(), DecDCTvlcCopyTable()
*/
int DecDCTvlc(const uint32_t *bs, uint32_t *buf);
/**
- * @brief Sets the maximum number of 32-bit words that a single call to
+ * @brief Sets the maximum amount of data to be decompressed.
+ *
+ * @details Sets the maximum number of 32-bit words that a single call to
* DecDCTvlc() will output. If size = 0, the entire frame will always be
* decoded in one shot.
*
+ * Note that DecDCTvlcStart() and DecDCTvlcContinue() do not use the value set
+ * by this function and instead expect the maximum size to be passed as an
+ * argument.
+ *
* @param size Maximum number of 32-bit words to output
- * @return Previously set value
+ * @return Previously set value
+ *
+ * @see DecDCTvlc()
*/
size_t DecDCTvlcSize(size_t size);
/**
- * @brief Copies the small (<1 KB) lookup table used by DecDCTvlcContinue(),
+ * @brief Moves the lookup table used by the .BS decompressor to the scratchpad
+ * region.
+ *
+ * @details Copies the small (<1 KB) lookup table used by DecDCTvlcContinue(),
* DecDCTvlcStart() and DecDCTvlc() (a DECDCTTAB structure) to the specified
* address. A copy of this table is always present in main RAM, however this
* function can be used to copy it to the scratchpad region to boost
@@ -287,7 +351,10 @@ size_t DecDCTvlcSize(size_t size);
void DecDCTvlcCopyTable(DECDCTTAB *addr);
/**
- * @brief Begins decompressing the contents of a .BS file (or of a single .STR
+ * @brief Decompresses or begins decompressing a .BS file into MDEC codes
+ * (alternate implementation).
+ *
+ * @details Begins decompressing the contents of a .BS file (or of a single STR
* frame) into a buffer that can be passed to DecDCTin(). This function uses a
* large (34 KB) lookup table that must be loaded into main RAM beforehand by
* calling DecDCTvlcBuild(), but does not use the GTE nor the scratchpad.
@@ -311,11 +378,16 @@ void DecDCTvlcCopyTable(DECDCTTAB *addr);
* @param max_size Maximum number of 32-bit words to output
* @param bs
* @return 0, 1 if more data needs to be output or -1 in case of failure
+ *
+ * @see DecDCTvlcContinue2(), DecDCTvlcBuild()
*/
int DecDCTvlcStart2(VLC_Context *ctx, uint32_t *buf, size_t max_size, const uint32_t *bs);
/**
- * @brief Resumes the decompression process started by DecDCTvlcStart2(). The
+ * @brief Resumes or finishes decompressing a .BS file into MDEC codes
+ * (alternate implementation).
+ *
+ * @details Resumes the decompression process started by DecDCTvlcStart2(). The
* state of the decompressor is contained entirely in the VLC_Context structure
* so an arbitrary number of bitstreams can be decoded concurrently (although
* the limited CPU power makes it impractical to do so) by keeping a separate
@@ -333,11 +405,15 @@ int DecDCTvlcStart2(VLC_Context *ctx, uint32_t *buf, size_t max_size, const uint
* @param buf
* @param max_size Maximum number of 32-bit words to output
* @return 0, 1 if more data needs to be output or -1 in case of failure
+ *
+ * @see DecDCTvlcStart2()
*/
int DecDCTvlcContinue2(VLC_Context *ctx, uint32_t *buf, size_t max_size);
/**
- * A wrapper around DecDCTvlcStart2() and DecDCTvlcContinue2() for
+ * @brief Decompresses a .BS file into MDEC codes (alternate implementation).
+ *
+ * @details A wrapper around DecDCTvlcStart2() and DecDCTvlcContinue2() for
* compatibility with the official SDK. This function uses an internal context;
* additionally, the maximum output buffer size is not passed as an argument
* but is instead set by calling DecDCTvlcSize2().
@@ -353,21 +429,35 @@ int DecDCTvlcContinue2(VLC_Context *ctx, uint32_t *buf, size_t max_size);
* @param buf
* @param table Pointer to decompressed table or 0 to use last table used
* @return 0, 1 if more data needs to be output or -1 in case of failure
+ *
+ * @see DecDCTvlcSize2(), DecDCTvlcBuild()
*/
int DecDCTvlc2(const uint32_t *bs, uint32_t *buf, DECDCTTAB2 *table);
/**
- * @brief Sets the maximum number of 32-bit words that a single call to
+ * @brief Sets the maximum amount of data to be decompressed (alternate
+ * implementation).
+ *
+ * @details Sets the maximum number of 32-bit words that a single call to
* DecDCTvlc2() will output. If size = 0, the entire frame will always be
* decoded in one shot.
*
+ * Note that DecDCTvlcStart2() and DecDCTvlcContinue2() do not use the value
+ * set by this function and instead expect the maximum size to be passed as an
+ * argument.
+ *
* @param size Maximum number of 32-bit words to output
- * @return Previously set value
+ * @return Previously set value
+ *
+ * @see DecDCTvlc2()
*/
size_t DecDCTvlcSize2(size_t size);
/**
- * @brief Generates the lookup table required by DecDCTvlcStart2(),
+ * @brief Generates the lookup table used by the alternate implementation of
+ * the .BS decompressor.
+ *
+ * @details Generates the lookup table required by DecDCTvlcStart2(),
* DecDCTvlcContinue2() and DecDCTvlc2() (a DECDCTTAB2 structure) into the
* specified buffer. Since the table is relatively large (34 KB), it is
* recommended to only generate it in a dynamically-allocated buffer when
diff --git a/libpsn00b/include/psxsio.h b/libpsn00b/include/psxsio.h
index 3f571d7..449e43a 100644
--- a/libpsn00b/include/psxsio.h
+++ b/libpsn00b/include/psxsio.h
@@ -1,64 +1,281 @@
+/*
+ * PSn00bSDK serial port library
+ * (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed
+ */
+
+/**
+ * @file psxsio.h
+ * @brief Serial port library header
+ *
+ * @details This library provides a custom API to access the PS1's serial port.
+ * Sending and receiving data is done fully asynchronously using a pair of
+ * 128-byte FIFOs kept in main RAM, with optional hardware flow control. More
+ * advanced use cases such as custom callbacks for each byte received are also
+ * supported.
+ *
+ * A BIOS TTY driver to redirect stdin/stdout (including BIOS messages as well
+ * as PSn00bSDK's own debug logging) to the serial port is also provided for
+ * debugging purposes.
+ */
+
#ifndef __PSXSIO_H
#define __PSXSIO_H
-#define SR_TXRDY 0x1
-#define SR_RXRDY 0x2
-#define SR_TXU 0x4
-#define SR_PERROR 0x8
-#define SR_OE 0x10
-#define SR_FE 0x20
-#define SR_DSR 0x80
-#define SR_CTS 0x100
-#define SR_IRQ 0x200
-
-#define SIO_TXRDY 0x1
-#define SIO_RXRDY 0x2
-#define SIO_TXU 0x4
-#define SIO_PERROR 0x8
-#define SIO_OE 0x10
-#define SIO_FE 0x20
-#define SIO_DSR 0x80
-#define SIO_CTS 0x100
-#define SIO_IRQ 0x200
-
-#define MR_CHLEN_5 0x00
-#define MR_CHLEN_6 0x04
-#define MR_CHLEN_7 0x08
-#define MR_CHLEN_8 0x0C
-#define MR_PEN 0x10
-#define MR_P_EVEN 0x30
-#define MR_SB_01 0x40
-#define MR_SB_10 0x80
-#define MR_SB_11 0xc0
-
-#define CR_TXEN 0x1
-#define CR_DTR 0x2
-#define CR_RXEN 0x4
-#define CR_BRK 0x8
-#define CR_INTRST 0x10
-#define CR_RTS 0x20
-#define CR_ERRRST 0x40
-#define CR_BUFSZ_1 0x00
-#define CR_BUFSZ_2 0x100
-#define CR_BUFSZ_4 0x200
-#define CR_BUFSZ_8 0x300
-#define CR_TXIEN 0x400
-#define CR_RXIEN 0x800
-#define CR_DSRIEN 0x1000
+#include <stdint.h>
+
+/* Enum and register definitions */
+
+typedef enum _SIO_StatusRegFlag {
+ SR_TXRDY = 1 << 0,
+ SR_RXRDY = 1 << 1,
+ SR_TXU = 1 << 2,
+ SR_PERROR = 1 << 3,
+ SR_OE = 1 << 4,
+ SR_FE = 1 << 5,
+ SR_DSR = 1 << 7,
+ SR_CTS = 1 << 8,
+ SR_IRQ = 1 << 9
+} SIO_StatusRegFlag;
+
+typedef enum _SIO_ModeRegFlag {
+ MR_BR_1 = 1 << 0,
+ MR_BR_16 = 2 << 0,
+ MR_BR_64 = 3 << 0,
+ MR_CHLEN_5 = 0 << 2,
+ MR_CHLEN_6 = 1 << 2,
+ MR_CHLEN_7 = 2 << 2,
+ MR_CHLEN_8 = 3 << 2,
+ MR_PEN = 1 << 4,
+ MR_P_EVEN = 1 << 5,
+ MR_SB_01 = 1 << 6,
+ MR_SB_10 = 2 << 6,
+ MR_SB_11 = 3 << 6
+} SIO_ModeRegFlag;
+
+typedef enum _SIO_ControlRegFlag {
+ CR_TXEN = 1 << 0,
+ CR_DTR = 1 << 1,
+ CR_RXEN = 1 << 2,
+ CR_BRK = 1 << 3,
+ CR_INTRST = 1 << 4,
+ CR_RTS = 1 << 5,
+ CR_ERRRST = 1 << 6,
+ CR_BUFSZ_1 = 0 << 8,
+ CR_BUFSZ_2 = 1 << 8,
+ CR_BUFSZ_4 = 2 << 8,
+ CR_BUFSZ_8 = 3 << 8,
+ CR_TXIEN = 1 << 10,
+ CR_RXIEN = 1 << 11,
+ CR_DSRIEN = 1 << 12
+} SIO_ControlRegFlag;
+
+typedef enum _SIO_FlowControl {
+ SIO_FC_NONE = 0,
+ SIO_FC_RTS_CTS = 1
+ //SIO_FC_DTR_DSR = 2
+} SIO_FlowControl;
+
+/* Public API */
#ifdef __cplusplus
extern "C" {
#endif
-int _sio_control(int cmd, int arg, int param);
-void AddSIO(int baud);
-void DelSIO(void);
+/**
+ * @brief Initializes the serial port driver.
+ *
+ * @details Resets the serial port, initializes the library's internal ring
+ * buffers and installs a serial IRQ handler. The given mode value (normally
+ * MR_CHLEN_8|MR_SB_01 for 8 data bits, 1 stop bit and no parity) is copied to
+ * the SIO_MODE register. Flow control is disabled by default (see
+ * SIO_SetFlowControl() for more details).
+ *
+ * This function must be called prior to using SIO_ReadByte(), SIO_ReadSync(),
+ * SIO_WriteByte(), SIO_WriteSync() or SIO_SetFlowControl(), and must not be
+ * called from an IRQ callback.
+ *
+ * @param baud Baud rate in bits per second
+ * @param mode Binary OR of SIO_ModeRegFlag enum members
+ *
+ * @see SIO_Quit()
+ */
+void SIO_Init(int baud, uint16_t mode);
+
+/**
+ * @brief Uninstalls the serial port driver.
+ *
+ * @details Resets the serial port and removes the IRQ callback added by
+ * SIO_Init(), restoring any previously installed handler if any. If SIO_Init()
+ * was previously invoked, calling SIO_Quit() before accessing serial port
+ * registers manually is highly recommended.
+ *
+ * @see SIO_Init()
+ */
+void SIO_Quit(void);
-void *Sio1Callback(void (*func)(void));
+/**
+ * @brief Sets the flow control mode.
+ *
+ * @details Changes the serial port's flow control mode. The following modes
+ * are available:
+ *
+ * - SIO_FC_NONE (default): do not assert RTS or DTR automatically and ignore
+ * DSR. Note that the hardware will still wait for CTS to be asserted before
+ * sending any data; there is no way to disable this behavior.
+ * - SIO_FC_RTS_CTS: assert RTS when the RX buffer is full and wait for CTS to
+ * be asserted before sending any data.
+ *
+ * The flow control mode shall only be changed while the TX and RX buffers are
+ * empty.
+ *
+ * @param mode
+ */
+void SIO_SetFlowControl(SIO_FlowControl mode);
-// ORIGINAL
-void WaitSIO(void);
-int kbhit();
+/**
+ * @brief Reads a byte from the RX buffer (blocking).
+ *
+ * @details Reads a byte from the RX buffer. If the buffer is empty, blocks
+ * indefinitely until a byte is received.
+ *
+ * WARNING: this function shall not be used in a critical section or IRQ
+ * callback as no data is sent or received while interrupts are disabled. It
+ * also lacks a timeout, so consider polling for new data using SIO_ReadByte2()
+ * or SIO_ReadSync(1) and implementing a timeout instead.
+ *
+ * @return Received byte
+ *
+ * @see SIO_ReadByte2(), SIO_ReadSync()
+ */
+int SIO_ReadByte(void);
+
+/**
+ * @brief Reads a byte from the RX buffer (non-blocking).
+ *
+ * @details Non-blocking variant of SIO_ReadByte(). Reads a byte from the RX
+ * buffer or returns -1 if the buffer is empty. Unlike SIO_ReadByte() this
+ * function is safe to use in a critical section (although no data will be
+ * received while interrupts are disabled).
+ *
+ * @return Received byte, -1 if no data is available
+ *
+ * @see SIO_ReadByte()
+ */
+int SIO_ReadByte2(void);
+
+/**
+ * @brief Waits for a byte to be received or returns the RX buffer's length.
+ *
+ * @details Waits for at least one byte to be available in the RX buffer (if
+ * mode = 0) or returns the length of the RX buffer (if mode = 1).
+ *
+ * WARNING: this function shall not be used in a critical section or IRQ
+ * callback as no data is sent or received while interrupts are disabled. Using
+ * SIO_ReadSync(0) is additionally discouraged as it lacks a timeout; consider
+ * polling for new data using SIO_ReadByte2() or SIO_ReadSync(1) and
+ * implementing a timeout instead.
+ *
+ * @param mode
+ * @return Number of RX bytes in the buffer
+ */
+int SIO_ReadSync(int mode);
+
+/**
+ * @brief Sets a callback for received bytes.
+ *
+ * @details Registers a function to be called whenever a byte is received. The
+ * received byte is passed as an argument to the callback, which shall then
+ * return a zero value to also store the byte in the RX buffer or a non-zero
+ * value to drop it. This can be used to e.g. filter or validate incoming data,
+ * or to bypass the library's RX buffer for custom buffering purposes.
+ *
+ * The callback will run in the exception handler's context, so it should be as
+ * fast as possible and shall not call any function that relies on interrupts
+ * being enabled.
+ *
+ * @param func
+ * @return Previously set callback or NULL
+ */
+void *SIO_ReadCallback(int (*func)(uint8_t));
+
+/**
+ * @brief Writes a byte to the TX buffer (blocking).
+ *
+ * @details Sends the given byte, or appends it to the TX buffer if the serial
+ * port is busy. If the buffer is full, blocks until the byte can be stored in
+ * the buffer (with a timeout).
+ *
+ * WARNING: this function shall not be used in a critical section or IRQ
+ * callback as no data is sent or received while interrupts are disabled.
+ *
+ * @param value
+ * @return Number of TX bytes previously pending, -1 in case of a timeout
+ *
+ * @see SIO_WriteByte2(), SIO_WriteSync()
+ */
+int SIO_WriteByte(uint8_t value);
+
+/**
+ * @brief Writes a byte to the TX buffer (non-blocking).
+ *
+ * @details Non-blocking variant of SIO_WriteByte(). Sends the given byte, or
+ * appends it to the TX buffer if the serial port is busy. If the buffer is
+ * full, returns -1 without actually sending the byte. Unlike SIO_WriteByte()
+ * this function is safe to use in a critical section (although no data will be
+ * sent while interrupts are disabled).
+ *
+ * @param value
+ * @return Number of TX bytes previously pending, -1 in case of failure
+ *
+ * @see SIO_WriteByte()
+ */
+int SIO_WriteByte2(uint8_t value);
+
+/**
+ * @brief Waits for all bytes to be sent or returns the TX buffer's length.
+ *
+ * @details Waits for all bytes pending in the TX buffer to be sent (if
+ * mode = 0) or returns the length of the TX buffer (if mode = 1).
+ *
+ * WARNING: this function shall not be used in a critical section or IRQ
+ * callback as no data is sent or received while interrupts are disabled.
+ *
+ * @param mode
+ * @return Number of TX bytes pending, -1 in case of a timeout (mode = 0)
+ */
+int SIO_WriteSync(int mode);
+
+/**
+ * @brief Installs the serial port TTY driver.
+ *
+ * @details Installs a BIOS file driver to redirect TTY stdin/stdout to the
+ * serial port. Uses SIO_Init() internally. The port is configured for 8 data
+ * bits, 1 stop bit and no parity.
+ *
+ * This function shall only be used for debugging purposes. Picking a high baud
+ * rate is recommended as all TTY writes are blocking and bypass the TX buffer.
+ *
+ * NOTE: some executable loaders, such as Unirom and Caetla, already replace
+ * the BIOS TTY driver with a custom one. Calling AddSIO() will break the
+ * built-in TTY functionality of these loaders.
+ *
+ * @param baud Baud rate in bits per second
+ *
+ * @see DelSIO()
+ */
+void AddSIO(int baud);
+
+/**
+ * @brief Removes the serial port TTY driver.
+ *
+ * @details Uninstalls the BIOS driver installed by AddSIO() and attempts to
+ * restore the default "dummy" TTY driver. Uses SIO_Quit() internally. Calling
+ * this function is not recommended as any further TTY usage may crash the
+ * system.
+ *
+ * @see AddSIO()
+ */
+void DelSIO(void);
#ifdef __cplusplus
}
diff --git a/libpsn00b/include/psxspu.h b/libpsn00b/include/psxspu.h
index cf78e3d..cdc3ac7 100644
--- a/libpsn00b/include/psxspu.h
+++ b/libpsn00b/include/psxspu.h
@@ -73,6 +73,20 @@ typedef struct _SpuCommonAttr {
SpuExtAttr cd, ext;
} SpuCommonAttr;
+/* Macros */
+
+#define getSPUAddr(addr) ((uint16_t) (((addr) + 7) / 8))
+#define getSPUSampleRate(rate) ((uint16_t) (((rate) * (1 << 12)) / 44100))
+
+#define getSPUADSR(ar, dr, sr, rr, sl) ( \
+ (sl) | \
+ ((dr) << 4) | \
+ ((ar) << 8) | \
+ ((rr) << 16) | \
+ ((sr) << 22) | \
+ (1 << 30) \
+)
+
/* "Useless" macros for official SDK compatibility */
#define SpuSetCommonMasterVolume(left, right) \
@@ -87,21 +101,29 @@ typedef struct _SpuCommonAttr {
((enable) ? (SPU_CTRL |= 0x0002) : (SPU_CTRL &= 0xfffd))
#define SpuSetReverbAddr(addr) \
- (SPU_REVERB_ADDR = ((addr) + 7) / 8)
+ (SPU_REVERB_ADDR = getSPUAddr(addr))
#define SpuSetIRQAddr(addr) \
- (SPU_IRQ_ADDR = ((addr) + 7) / 8)
+ (SPU_IRQ_ADDR = getSPUAddr(addr))
#define SpuSetVoiceVolume(ch, left, right) \
(SPU_CH_VOL_L(ch) = (left), SPU_CH_VOL_R(ch) = (right))
#define SpuSetVoicePitch(ch, pitch) \
(SPU_CH_FREQ(ch) = (pitch))
#define SpuSetVoiceStartAddr(ch, addr) \
- (SPU_CH_ADDR(ch) = ((addr) + 7) / 8)
-#define SpuSetVoiceADSR(ch, ar, dr, sr, rr, sl) \
- (SPU_CH_ADSR(ch) = ((sl)) | ((dr) << 4) | ((ar) << 8) | ((rr) << 16) | ((sr) << 22) | (1 << 30))
+ (SPU_CH_ADDR(ch) = getSPUAddr(addr))
+#define SpuSetVoiceADSR(ch, ar, dr, sr, rr, sl) ( \
+ SPU_CH_ADSR1(ch) = (sl) | ((dr) << 4) | ((ar) << 8), \
+ SPU_CH_ADSR2(ch) = (rr) | ((sr) << 6) | (1 << 14) \
+)
#define SpuSetKey(enable, voice_bit) \
- ((enable) ? (SPU_KEY_ON = (voice_bit)) : (SPU_KEY_OFF = (voice_bit)))
+ ((enable) ? ( \
+ SPU_KEY_ON1 = (uint16_t) (voice_bit), \
+ SPU_KEY_ON2 = (uint16_t) ((voice_bit) >> 16) \
+ ) : ( \
+ SPU_KEY_OFF1 = (uint16_t) (voice_bit), \
+ SPU_KEY_OFF2 = (uint16_t) ((voice_bit) >> 16) \
+ ))
/* Public API */
@@ -111,8 +133,9 @@ extern "C" {
void SpuInit(void);
-void SpuRead(uint32_t *data, size_t size);
-void SpuWrite(const uint32_t *data, size_t size);
+size_t SpuRead(uint32_t *data, size_t size);
+size_t SpuWrite(const uint32_t *data, size_t size);
+size_t SpuWritePartly(const uint32_t *data, size_t size);
SPU_TransferMode SpuSetTransferMode(SPU_TransferMode mode);
uint32_t SpuSetTransferStartAddr(uint32_t addr);
int SpuIsTransferCompleted(int mode);
diff --git a/libpsn00b/include/stdlib.h b/libpsn00b/include/stdlib.h
index f0753c1..049d067 100644
--- a/libpsn00b/include/stdlib.h
+++ b/libpsn00b/include/stdlib.h
@@ -31,17 +31,19 @@ extern "C" {
extern int __argc;
extern const char **__argv;
+void abort(void);
+
int rand(void);
-void srand(unsigned long seed);
+void srand(int seed);
int abs(int j);
long labs(long i);
-long long strtoll(const char *nptr, char **endptr, int base);
-long strtol(const char *nptr, char **endptr, int base);
-long double strtold(const char *nptr, char **endptr);
-double strtod(const char *nptr, char **endptr);
+long strtol(const char *nptr, char **endptr, int base);
+long long strtoll(const char *nptr, char **endptr, int base);
float strtof(const char *nptr, char **endptr);
+double strtod(const char *nptr, char **endptr);
+long double strtold(const char *nptr, char **endptr);
void InitHeap(void *addr, size_t size);
void *sbrk(ptrdiff_t incr);