diff options
| author | John "Lameguy" Wilbert Villamor <lameguy64@gmail.com> | 2022-11-03 10:14:22 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-03 10:14:22 +0800 |
| commit | 4139331d233b7a962e747c5564fa68a285f81cc8 (patch) | |
| tree | d4d3374afd5e36e8580cc424ab2c63ee9e7d357c /libpsn00b/include | |
| parent | e08a3d9366f8ca14a76b3dd569dac1fb9f569748 (diff) | |
| parent | 37d963f724113e45d15aa9b8ee86baa9c4362b8f (diff) | |
| download | psn00bsdk-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.h | 23 | ||||
| -rw-r--r-- | libpsn00b/include/dlfcn.h | 237 | ||||
| -rw-r--r-- | libpsn00b/include/hwregs_a.inc | 22 | ||||
| -rw-r--r-- | libpsn00b/include/hwregs_c.h | 21 | ||||
| -rw-r--r-- | libpsn00b/include/inline_c.h | 173 | ||||
| -rw-r--r-- | libpsn00b/include/psxcd.h | 828 | ||||
| -rw-r--r-- | libpsn00b/include/psxetc.h | 196 | ||||
| -rw-r--r-- | libpsn00b/include/psxgpu.h | 105 | ||||
| -rw-r--r-- | libpsn00b/include/psxgte.h | 205 | ||||
| -rw-r--r-- | libpsn00b/include/psxpress.h | 158 | ||||
| -rw-r--r-- | libpsn00b/include/psxsio.h | 319 | ||||
| -rw-r--r-- | libpsn00b/include/psxspu.h | 39 | ||||
| -rw-r--r-- | libpsn00b/include/stdlib.h | 12 |
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); |
