diff options
| author | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-12-28 12:18:29 +0100 |
|---|---|---|
| committer | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-12-28 12:18:29 +0100 |
| commit | 7e350980d5c09bbc81a0de01bf016a87ecfc4feb (patch) | |
| tree | 26b403d12eea34a4644b3d147f00e1d1455e7f52 | |
| parent | eaec942f56ceec9c14de5c4185a02602abadd50a (diff) | |
Add CdUnlock() and DMA priority API
| -rw-r--r-- | libpsn00b/include/psxcd.h | 57 | ||||
| -rw-r--r-- | libpsn00b/include/psxetc.h | 41 | ||||
| -rw-r--r-- | libpsn00b/psxcd/common.c | 6 | ||||
| -rw-r--r-- | libpsn00b/psxcd/misc.c | 137 | ||||
| -rw-r--r-- | libpsn00b/psxetc/interrupts.c | 25 | ||||
| -rw-r--r-- | libpsn00b/psxgpu/common.c | 36 | ||||
| -rw-r--r-- | libpsn00b/psxgpu/image.c | 10 | ||||
| -rw-r--r-- | libpsn00b/psxpress/mdec.c | 35 | ||||
| -rw-r--r-- | libpsn00b/psxspu/common.c | 14 |
9 files changed, 258 insertions, 103 deletions
diff --git a/libpsn00b/include/psxcd.h b/libpsn00b/include/psxcd.h index 503bc83..78c90db 100644 --- a/libpsn00b/include/psxcd.h +++ b/libpsn00b/include/psxcd.h @@ -811,6 +811,48 @@ int CdMode(void); int CdStatus(void); /** + * @brief Returns the CD-ROM controller's region code. + * + * @details Reads region information from the drive using a CdlTest command. + * This can be used to reliably determine the system's region without having to + * resort to workarounds like probing the BIOS ROM. + * + * This function may return incorrect results and trigger error callbacks on + * emulators or consoles equipped with CD-ROM drive emulation devices such as + * the PSIO. It is not affected by modchips. + * + * @return Region code or 0 if the region cannot be determined + */ +CdlRegionCode CdGetRegion(void); + +/** + * @brief Attempts to disable the CD-ROM controller's region check. + * + * @details Sends undocumented commands to the drive in an attempt to disable + * the region string check, in order to allow reading data from non-PS1 discs + * as well as CD-Rs without needing a modchip. As unlocking commands are region + * specific, the drive's region must be obtained beforehand using CdGetRegion() + * and passed to this function. The unlock persists even if the lid is opened, + * but not if a CdlReset command is issued. + * + * Unlocking is only supported on US, European and Net Yaroze consoles (not on + * Japanese models, devkits and most emulators). This function will return 1 + * without doing anything if CdlRegionDebug is passed as region, as debug + * consoles can already read unlicensed discs. + * + * NOTE: if any callbacks were set using CdReadyCallback() or CdSyncCallback() + * prior to calling CdUnlock(), they will be called with an error code as part + * of the unlocking sequence, even if the unlock was successful. It is thus + * recommended to call this function before setting any callbacks. + * + * @param region + * @return 1 if the drive was successfully unlocked, 0 otherwise + * + * @see CdGetRegion() + */ +int CdUnlock(CdlRegionCode region); + +/** * @brief Retrieves the disc's table of contents. * * @details Retrieves the track entries from a CD's table of contents (TOC). The @@ -832,21 +874,6 @@ int CdStatus(void); int CdGetToc(CdlLOC *toc); /** - * @brief Returns the CD-ROM controller's region code. - * - * @details Attempts to fetch region information from the drive using a CdlTest - * command. This can be used to reliably determine the system's region without - * having to resort to workarounds like probing the BIOS ROM. - * - * This function may return incorrect results on emulators or consoles equipped - * with CD-ROM drive emulation devices such as the PSIO. It is not affected by - * modchips. - * - * @return Region code or 0 if the region cannot be determined - */ -CdlRegionCode CdGetRegion(void); - -/** * @brief Sets the CD-ROM volume mixing matrix. * * @details Sets the volume levels of the CD-ROM drive's audio output (used for diff --git a/libpsn00b/include/psxetc.h b/libpsn00b/include/psxetc.h index ae4611e..ebf7966 100644 --- a/libpsn00b/include/psxetc.h +++ b/libpsn00b/include/psxetc.h @@ -156,19 +156,50 @@ void *DMACallback(DMA_Channel dma, void (*func)(void)); void *GetDMACallback(DMA_Channel dma); /** - * @brief Initializes the interrupt dispatcher. + * @brief Enables, disables or sets the priority of a DMA channel. + * + * @details Enables the specified DMA channel and configures its priority (if + * priority >= 0) or disables it (if priority = -1). The priority value must be + * in 0-7 range, with 0 being the highest priority and 7 the lowest. + * + * All channels are disabled upon calling ResetCallback(); most libraries will + * re-enable them as needed. By default the priority is set to 3 for all + * channels. + * + * @param dma + * @param priority Priority in 0-7 range or -1 to disable the channel + * @return Previously set priority in 0-7 range, -1 if the channel was disabled + */ +int SetDMAPriority(DMA_Channel dma, int priority); + +/** + * @brief Gets the priority of a DMA channel. + * + * @details Returns the currently set priority value for the specified DMA + * channel in 0-7 range, with 0 being the highest priority and 7 the lowest. + * Returns -1 if the channel is not enabled. + * + * @param dma + * @return Priority in 0-7 range, -1 if the channel is disabled + * + * @see SetDMAPriority() + */ +int GetDMAPriority(DMA_Channel dma); + +/** + * @brief Initializes the interrupt dispatcher and DMA controller. * * @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. + * interrupts to the library, clears all registered callbacks and disables all + * DMA channels. 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 + * @return 0 or -1 if the dispatcher was already initialized */ int ResetCallback(void); diff --git a/libpsn00b/psxcd/common.c b/libpsn00b/psxcd/common.c index 8b8030b..c8f01d1 100644 --- a/libpsn00b/psxcd/common.c +++ b/libpsn00b/psxcd/common.c @@ -208,6 +208,9 @@ int CdInit(void) { BUS_CD_CFG = 0x00020943; + SetDMAPriority(DMA_CD, 3); + DMA_CHCR(DMA_CD) = 0x00000000; // Stop DMA + CD_REG(0) = 1; CD_REG(3) = 0x1f; // Acknowledge all IRQs CD_REG(2) = 0x1f; // Enable all IRQs @@ -217,9 +220,6 @@ int CdInit(void) { CdlATV mix = { 0x80, 0x00, 0x80, 0x00 }; CdMix(&mix); - DMA_DPCR |= 0x0000b000; // Enable DMA3 - DMA_CHCR(DMA_CD) = 0x00000000; // Stop DMA3 - _last_mode = 0; _ack_pending = 0; _sync_pending = 0; diff --git a/libpsn00b/psxcd/misc.c b/libpsn00b/psxcd/misc.c index 8fd2a4d..fc87676 100644 --- a/libpsn00b/psxcd/misc.c +++ b/libpsn00b/psxcd/misc.c @@ -12,11 +12,23 @@ #define DATA_SYNC_TIMEOUT 0x100000 -/* Private types */ - -typedef struct { - uint8_t status, first_track, last_track; -} TrackInfo; +/* Unlock command strings */ + +static char *_unlock_strings[] = { + "", + "Licensed by", + "Sony", + "Computer", + "Entertainment", + "", + "" +}; + +static const char *_unlock_regions[] = { + "of America", // CdlRegionSCEA + "(Europe)", // CdlRegionSCEE + "World wide" // CdlRegionSCEW +}; /* Sector DMA transfer functions */ @@ -77,52 +89,40 @@ int CdPosToInt(const CdlLOC *p) { ) - 150; } -/* Misc. functions */ +/* Drive unlocking API */ -int CdGetToc(CdlLOC *toc) { - TrackInfo track_info; - - if (!CdCommand(CdlGetTN, 0, 0, (uint8_t *) &track_info)) - return 0; - if (CdSync(1, 0) != CdlComplete) - return 0; - - int first = btoi(track_info.first_track); - int tracks = btoi(track_info.last_track) + 1 - first; - //assert(first == 1); - - for (int i = 0; i < tracks; i++) { - uint8_t track = itob(first + i); +CdlRegionCode CdGetRegion(void) { + uint8_t param; + uint8_t result[16]; - if (!CdCommand(CdlGetTD, &track, 1, (uint8_t *) &toc[i])) - return 0; - if (CdSync(1, 0) != CdlComplete) - return 0; + // Firmware version C0 does not support test command 0x22 to retrieve the + // region, but it was only used in the SCPH-1000 Japanese model. Version D1 + // (and possibly others?) is used in debug consoles. + // https://psx-spx.consoledev.net/cdromdrive/#19h20h-int3yymmddver + // https://psx-spx.consoledev.net/cdromdrive/#19h22h-int3for-europe + param = 0x20; + memset(result, 0, 4); - toc[i].sector = 0; - toc[i].track = track; + if (!CdCommand(CdlTest, ¶m, 1, result)) { + _sdk_log("failed to probe drive firmware version\n"); + return CdlRegionUnknown; } - return tracks; -} - -CdlRegionCode CdGetRegion(void) { - uint8_t param = 0x22; - uint8_t result[16]; + _sdk_log("drive firmware version: 0x%02x\n", result[3]); + if (result[3] == 0xc0) + return CdlRegionSCEI; + if (result[3] >= 0xd0) + return CdlRegionDebug; - // Test command 0x22 is unsupported in firmware version C0, which was used - // exclusively in the SCPH-1000 Japanese model. It's thus safe to assume - // that the console is Japanese if the command returns a valid error. - // https://psx-spx.consoledev.net/cdromdrive/#19h22h-int3for-europe + param = 0x22; memset(result, 0, 16); if (!CdCommand(CdlTest, ¶m, 1, result)) { _sdk_log("failed to probe drive region\n"); - return (result[1] == 0x10) ? CdlRegionSCEI : CdlRegionUnknown; + return CdlRegionUnknown; } _sdk_log("drive region: %s\n", result); - if (!strcmp(result, "for Japan")) return CdlRegionSCEI; if (!strcmp(result, "for U/C")) @@ -137,6 +137,67 @@ CdlRegionCode CdGetRegion(void) { return CdlRegionUnknown; } +int CdUnlock(CdlRegionCode region) { + if (region <= CdlRegionSCEI) + return 0; + if (region >= CdlRegionDebug) + return 1; + + // This is by far the most efficient way to do it. + _unlock_strings[5] = _unlock_regions[region - CdlRegionSCEA]; + + for (int i = 0; i < 7; i++) { + uint8_t result[4]; + + if (!CdCommand( + 0x50 + i, + _unlock_strings[i], + strlen(_unlock_strings[i]), + result + )) + return 0; + + if (!(result[0] & CdlStatError) || (result[1] != 0x40)) { + _sdk_log("unlock failed, status=0x%02x, code=0x%02x\n", result[0], result[1]); + return 0; + } + } + + _sdk_log("unlock successful\n"); + return CdCommand(CdlNop, 0, 0, 0); +} + +/* Misc. functions */ + +int CdGetToc(CdlLOC *toc) { + uint8_t result[4]; + + if (!CdCommand(CdlGetTN, 0, 0, result)) + return 0; + if (CdSync(1, 0) != CdlComplete) + return 0; + + int first = btoi(result[1]); + int tracks = btoi(result[2]) + 1 - first; + //assert(first == 1); + + for (int i = 0; i < tracks; i++) { + uint8_t track = itob(first + i); + + if (!CdCommand(CdlGetTD, &track, 1, result)) + return 0; + if (CdSync(1, 0) != CdlComplete) + return 0; + + toc[i].minute = result[1]; + toc[i].second = result[2]; + toc[i].sector = 0; + toc[i].track = track; + } + + return tracks; +} + int CdMix(const CdlATV *vol) { CD_REG(0) = 2; CD_REG(2) = vol->val0; diff --git a/libpsn00b/psxetc/interrupts.c b/libpsn00b/psxetc/interrupts.c index f2a273c..7c8b206 100644 --- a/libpsn00b/psxetc/interrupts.c +++ b/libpsn00b/psxetc/interrupts.c @@ -157,6 +157,31 @@ void *GetDMACallback(DMA_Channel dma) { return _dma_handlers[dma]; } +/* DMA channel priority API */ + +int SetDMAPriority(DMA_Channel dma, int priority) { + if ((dma < 0) || (dma >= NUM_DMA_CHANNELS)) + return -1; + + uint32_t dpcr = DMA_DPCR; + uint32_t channel = dpcr >> (dma * 4); + + dpcr &= ~(0xf << (dma * 4)); + if (priority >= 0) + dpcr |= ((priority & 7) | 8) << (dma * 4); + + DMA_DPCR = dpcr; + return (channel & 8) ? (channel & 7) : -1; +} + +int GetDMAPriority(DMA_Channel dma) { + if ((dma < 0) || (dma >= NUM_DMA_CHANNELS)) + return -1; + + uint32_t channel = DMA_DPCR >> (dma * 4); + return (channel & 8) ? (channel & 7) : -1; +} + /* Hook installation/removal API */ int ResetCallback(void) { diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index e41bd31..e66e46c 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -46,7 +46,7 @@ static void _vblank_handler(void) { } static void _gpu_dma_handler(void) { - //while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) + //while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); @@ -85,9 +85,10 @@ void ResetGraph(int mode) { return; } - DMA_DPCR |= 0x0b000b00; // Enable DMA2 and DMA6 - DMA_CHCR(2) = 0x00000201; // Stop DMA2 - DMA_CHCR(6) = 0x00000200; // Stop DMA6 + SetDMAPriority(DMA_GPU, 3); + SetDMAPriority(DMA_OTC, 3); + DMA_CHCR(DMA_GPU) = 0x00000201; // Stop DMA + DMA_CHCR(DMA_OTC) = 0x00000200; // Stop DMA if (mode == 1) { GPU_GP1 = 0x01000000; // Reset command buffer @@ -225,7 +226,7 @@ int DrawSync(int mode) { if (!_queue_length) { // Wait for any DMA transfer to finish if DMA is enabled. if (GPU_GP1 & (3 << 29)) { - while (!(GPU_GP1 & (1 << 28)) || (DMA_CHCR(2) & (1 << 24))) + while (!(GPU_GP1 & (1 << 28)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) __asm__ volatile(""); } @@ -251,11 +252,11 @@ void *DrawSyncCallback(void (*func)(void)) { /* OT and primitive drawing API */ void ClearOTagR(uint32_t *ot, size_t length) { - DMA_MADR(6) = (uint32_t) &ot[length - 1]; - DMA_BCR(6) = length & 0xffff; - DMA_CHCR(6) = 0x11000002; + DMA_MADR(DMA_OTC) = (uint32_t) &ot[length - 1]; + DMA_BCR(DMA_OTC) = length & 0xffff; + DMA_CHCR(DMA_OTC) = 0x11000002; - while (DMA_CHCR(6) & (1 << 24)) + while (DMA_CHCR(DMA_OTC) & (1 << 24)) __asm__ volatile(""); } @@ -283,13 +284,14 @@ void DrawPrim(const uint32_t *pri) { // NOTE: if length >= DMA_CHUNK_LENGTH then it also has to be a multiple of // DMA_CHUNK_LENGTH, otherwise the DMA channel will get stuck waiting for // more data indefinitely. - DMA_MADR(2) = (uint32_t) &pri[1]; + DMA_MADR(DMA_GPU) = (uint32_t) &pri[1]; if (length < DMA_CHUNK_LENGTH) - DMA_BCR(2) = 0x00010000 | length; + DMA_BCR(DMA_GPU) = 0x00010000 | length; else - DMA_BCR(2) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + DMA_BCR(DMA_GPU) = DMA_CHUNK_LENGTH | + ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(2) = 0x01000201; + DMA_CHCR(DMA_GPU) = 0x01000201; } int DrawOTag(const uint32_t *ot) { @@ -299,12 +301,12 @@ int DrawOTag(const uint32_t *ot) { void DrawOTag2(const uint32_t *ot) { GPU_GP1 = 0x04000002; - while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) + while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) __asm__ volatile(""); - DMA_MADR(2) = (uint32_t) ot; - DMA_BCR(2) = 0; - DMA_CHCR(2) = 0x01000401; + DMA_MADR(DMA_GPU) = (uint32_t) ot; + DMA_BCR(DMA_GPU) = 0; + DMA_CHCR(DMA_GPU) = 0x01000401; } /* Misc. functions */ diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index fc018a4..96f5366 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -9,6 +9,7 @@ #include <stdint.h> #include <assert.h> +#include <psxetc.h> #include <psxgpu.h> #include <hwregs_c.h> @@ -49,13 +50,14 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { // Enable DMA request, route to GP0 (2) or from GPU_READ (3) GPU_GP1 = 0x04000002 | (write ^ 1); - DMA_MADR(2) = (uint32_t) data; + DMA_MADR(DMA_GPU) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) - DMA_BCR(2) = 0x00010000 | length; + DMA_BCR(DMA_GPU) = 0x00010000 | length; else - DMA_BCR(2) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + DMA_BCR(DMA_GPU) = DMA_CHUNK_LENGTH | + ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(2) = 0x01000200 | write; + DMA_CHCR(DMA_GPU) = 0x01000200 | write; } /* VRAM transfer API */ diff --git a/libpsn00b/psxpress/mdec.c b/libpsn00b/psxpress/mdec.c index d15a4db..3596188 100644 --- a/libpsn00b/psxpress/mdec.c +++ b/libpsn00b/psxpress/mdec.c @@ -5,6 +5,7 @@ #include <stdint.h> #include <assert.h> +#include <psxetc.h> #include <psxapi.h> #include <psxpress.h> #include <hwregs_c.h> @@ -86,11 +87,13 @@ static const DECDCTENV _default_mdec_env = { void DecDCTReset(int mode) { FastEnterCriticalSection(); - DMA_DPCR |= 0x000000bb; // Enable DMA0 and DMA1 - DMA_CHCR(0) = 0x00000201; // Stop DMA0 - DMA_CHCR(1) = 0x00000200; // Stop DMA1 - MDEC1 = 0x80000000; // Reset MDEC - MDEC1 = 0x60000000; // Enable DMA in/out requests + SetDMAPriority(DMA_MDEC_IN, 3); + SetDMAPriority(DMA_MDEC_OUT, 3); + DMA_CHCR(DMA_MDEC_IN) = 0x00000201; // Stop DMA + DMA_CHCR(DMA_MDEC_OUT) = 0x00000200; // Stop DMA + + MDEC1 = 0x80000000; // Reset MDEC + MDEC1 = 0x60000000; // Enable DMA in/out requests FastExitCriticalSection(); if (!mode) @@ -131,13 +134,14 @@ void DecDCTinRaw(const uint32_t *data, size_t length) { length += DMA_CHUNK_LENGTH - 1; } - DMA_MADR(0) = (uint32_t) data; + DMA_MADR(DMA_MDEC_IN) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) - DMA_BCR(0) = 0x00010000 | length; + DMA_BCR(DMA_MDEC_IN) = 0x00010000 | length; else - DMA_BCR(0) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + DMA_BCR(DMA_MDEC_IN) = DMA_CHUNK_LENGTH | + ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(0) = 0x01000201; + DMA_CHCR(DMA_MDEC_IN) = 0x01000201; } int DecDCTinSync(int mode) { @@ -161,21 +165,22 @@ void DecDCTout(uint32_t *data, size_t length) { length += DMA_CHUNK_LENGTH - 1; } - DMA_MADR(1) = (uint32_t) data; + DMA_MADR(DMA_MDEC_OUT) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) - DMA_BCR(1) = 0x00010000 | length; + DMA_BCR(DMA_MDEC_OUT) = 0x00010000 | length; else - DMA_BCR(1) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + DMA_BCR(DMA_MDEC_OUT) = DMA_CHUNK_LENGTH | + ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(1) = 0x01000200; + DMA_CHCR(DMA_MDEC_OUT) = 0x01000200; } int DecDCToutSync(int mode) { if (mode) - return (DMA_CHCR(1) >> 24) & 1; + return (DMA_CHCR(DMA_MDEC_OUT) >> 24) & 1; for (int i = MDEC_SYNC_TIMEOUT; i; i--) { - if (!(DMA_CHCR(1) & (1 << 24))) + if (!(DMA_CHCR(DMA_MDEC_OUT) & (1 << 24))) return 0; } diff --git a/libpsn00b/psxspu/common.c b/libpsn00b/psxspu/common.c index 45654ad..6ccbef4 100644 --- a/libpsn00b/psxspu/common.c +++ b/libpsn00b/psxspu/common.c @@ -5,6 +5,7 @@ #include <stdint.h> #include <assert.h> +#include <psxetc.h> #include <psxspu.h> #include <hwregs_c.h> @@ -59,13 +60,14 @@ static size_t _dma_transfer(uint32_t *data, size_t length, int write) { SPU_CTRL |= ctrl; _wait_status(0x0030, ctrl); - DMA_MADR(4) = (uint32_t) data; + DMA_MADR(DMA_SPU) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) - DMA_BCR(4) = 0x00010000 | length; + DMA_BCR(DMA_SPU) = 0x00010000 | length; else - DMA_BCR(4) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + DMA_BCR(DMA_SPU) = DMA_CHUNK_LENGTH | + ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(4) = 0x01000200 | write; + DMA_CHCR(DMA_SPU) = 0x01000200 | write; return length; } @@ -130,8 +132,8 @@ void SpuInit(void) { SPU_EXT_VOL_L = 0; SPU_EXT_VOL_R = 0; - DMA_DPCR |= 0x000b0000; // Enable DMA4 - DMA_CHCR(4) = 0x00000201; // Stop DMA4 + SetDMAPriority(DMA_SPU, 3); + DMA_CHCR(DMA_SPU) = 0x00000201; // Stop DMA SPU_DMA_CTRL = 0x0004; // Reset transfer mode SPU_CTRL = 0xc001; // Enable SPU, DAC, CD audio, disable DMA request |
