aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2022-10-27 12:04:11 +0200
committerspicyjpeg <thatspicyjpeg@gmail.com>2022-10-27 12:04:11 +0200
commit4dbf47f129a55428b90df2805228fbd481e1d117 (patch)
tree5f85468522e96e00157a290ad3b809d1c3633b1d
parent5f25c0bf306d316c87fca9d3fe160d6661be230d (diff)
downloadpsn00bsdk-4dbf47f129a55428b90df2805228fbd481e1d117.tar.gz
Fix SPU_TRANSFER_BY_IO mode, add IRQ/DMA enums
-rw-r--r--libpsn00b/include/psxcd.h4
-rw-r--r--libpsn00b/include/psxetc.h35
-rw-r--r--libpsn00b/include/psxspu.h5
-rw-r--r--libpsn00b/psxcd/psxcd.c2
-rw-r--r--libpsn00b/psxetc/interrupts.c8
-rw-r--r--libpsn00b/psxpress/vlc.s9
-rw-r--r--libpsn00b/psxspu/common.c93
7 files changed, 107 insertions, 49 deletions
diff --git a/libpsn00b/include/psxcd.h b/libpsn00b/include/psxcd.h
index 0460f20..bd89f53 100644
--- a/libpsn00b/include/psxcd.h
+++ b/libpsn00b/include/psxcd.h
@@ -136,7 +136,7 @@ extern "C" {
int CdInit(void);
CdlLOC* CdIntToPos(int i, CdlLOC *p);
-int CdPosToInt(CdlLOC *p);
+int CdPosToInt(const CdlLOC *p);
int CdGetToc(CdlLOC *toc);
int CdControl(uint8_t com, const void *param, uint8_t *result);
@@ -159,7 +159,7 @@ uint32_t CdReadCallback(CdlCB func);
int CdStatus(void);
int CdMode(void);
-int CdMix(CdlATV *vol);
+int CdMix(const CdlATV *vol);
/* ORIGINAL CODE */
CdlDIR* CdOpenDir(const char* path);
diff --git a/libpsn00b/include/psxetc.h b/libpsn00b/include/psxetc.h
index 24485d9..3b18784 100644
--- a/libpsn00b/include/psxetc.h
+++ b/libpsn00b/include/psxetc.h
@@ -6,16 +6,43 @@
#ifndef __PSXETC_H
#define __PSXETC_H
+/* IRQ and DMA channel definitions */
+
+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 */
#ifdef __cplusplus
extern "C" {
#endif
-void *InterruptCallback(int irq, void (*func)(void));
-void *GetInterruptCallback(int irq);
-void *DMACallback(int dma, void (*func)(void));
-void *GetDMACallback(int dma);
+void *InterruptCallback(IRQ_Channel irq, void (*func)(void));
+void *GetInterruptCallback(IRQ_Channel irq);
+void *DMACallback(DMA_Channel dma, void (*func)(void));
+void *GetDMACallback(DMA_Channel dma);
int ResetCallback(void);
void RestartCallback(void);
diff --git a/libpsn00b/include/psxspu.h b/libpsn00b/include/psxspu.h
index 7858e88..05737f7 100644
--- a/libpsn00b/include/psxspu.h
+++ b/libpsn00b/include/psxspu.h
@@ -125,8 +125,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/psxcd/psxcd.c b/libpsn00b/psxcd/psxcd.c
index d8d0686..9392d30 100644
--- a/libpsn00b/psxcd/psxcd.c
+++ b/libpsn00b/psxcd/psxcd.c
@@ -208,7 +208,7 @@ CdlLOC *CdIntToPos(int i, CdlLOC *p) {
}
-int CdPosToInt(CdlLOC *p)
+int CdPosToInt(const CdlLOC *p)
{
return ((75*(btoi(p->minute)*60))+(75*btoi(p->second))+btoi(p->sector))-150;
}
diff --git a/libpsn00b/psxetc/interrupts.c b/libpsn00b/psxetc/interrupts.c
index cc9d12c..0d926c4 100644
--- a/libpsn00b/psxetc/interrupts.c
+++ b/libpsn00b/psxetc/interrupts.c
@@ -98,7 +98,7 @@ static void _global_dma_handler(void) {
/* IRQ and DMA handler API */
-void *InterruptCallback(int irq, void (*func)(void)) {
+void *InterruptCallback(IRQ_Channel irq, void (*func)(void)) {
if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS))
return 0;
@@ -115,14 +115,14 @@ void *InterruptCallback(int irq, void (*func)(void)) {
return old_callback;
}
-void *GetInterruptCallback(int irq) {
+void *GetInterruptCallback(IRQ_Channel irq) {
if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS))
return 0;
return _irq_handlers[irq];
}
-void *DMACallback(int dma, void (*func)(void)) {
+void *DMACallback(DMA_Channel dma, void (*func)(void)) {
if ((dma < 0) || (dma >= NUM_DMA_CHANNELS))
return 0;
@@ -150,7 +150,7 @@ void *DMACallback(int dma, void (*func)(void)) {
return old_callback;
}
-void *GetDMACallback(int dma) {
+void *GetDMACallback(DMA_Channel dma) {
if ((dma < 0) || (dma >= NUM_DMA_CHANNELS))
return 0;
diff --git a/libpsn00b/psxpress/vlc.s b/libpsn00b/psxpress/vlc.s
index 75e33d3..f3a1c67 100644
--- a/libpsn00b/psxpress/vlc.s
+++ b/libpsn00b/psxpress/vlc.s
@@ -145,17 +145,16 @@ _vlc_skip_context_load:
li $v0, -1
.Lprocess_ac_coefficient: # if (coeff_index)
- # Start counting the number of leading zeroes/ones using the GTE. This
- # takes 2 more cycles.
- mtc2 $t0, $30
-
- # Check whether the prefix code is one of the shorter, more common ones.
+ # Check whether the prefix code is one of the shorter, more common ones,
+ # and start counting the number of leading zeroes/ones using the GTE (which
+ # takes 2 more cycles).
srl $v0, $t0, 30
li $v1, 3
beq $v0, $v1, .Lac_prefix_11
li $v1, 2
beq $v0, $v1, .Lac_prefix_10
li $v1, 1
+ mtc2 $t0, $30
beq $v0, $v1, .Lac_prefix_01
nop
diff --git a/libpsn00b/psxspu/common.c b/libpsn00b/psxspu/common.c
index 1613ca9..ca4b201 100644
--- a/libpsn00b/psxspu/common.c
+++ b/libpsn00b/psxspu/common.c
@@ -8,6 +8,8 @@
#include <psxspu.h>
#include <hwregs_c.h>
+#define _min(x, y) (((x) < (y)) ? (x) : (y))
+
#define WRITABLE_AREA_ADDR 0x200
#define DMA_CHUNK_LENGTH 16
#define STATUS_TIMEOUT 0x100000
@@ -28,7 +30,7 @@ static void _wait_status(uint16_t mask, uint16_t value) {
_sdk_log("status register timeout (0x%04x)\n", SPU_STAT);
}
-static void _dma_transfer(uint32_t *data, size_t length, int write) {
+static size_t _dma_transfer(uint32_t *data, size_t length, int write) {
if (length % 4)
_sdk_log("can't transfer a number of bytes that isn't multiple of 4\n");
@@ -38,8 +40,7 @@ static void _dma_transfer(uint32_t *data, size_t length, int write) {
length += DMA_CHUNK_LENGTH - 1;
}
- SPU_DMA_CTRL = 0x0004; // Reset transfer mode
- SPU_CTRL &= 0xffcf; // Disable DMA request
+ SPU_CTRL &= 0xffcf; // Disable DMA request
_wait_status(0x0030, 0x0000);
// Enable DMA request for writing (2) or reading (3)
@@ -47,7 +48,7 @@ static void _dma_transfer(uint32_t *data, size_t length, int write) {
SPU_ADDR = _transfer_addr;
SPU_CTRL |= ctrl;
- _wait_status(0x0430, ctrl);
+ _wait_status(0x0030, ctrl);
DMA_MADR(4) = (uint32_t) data;
if (length < DMA_CHUNK_LENGTH)
@@ -56,6 +57,42 @@ static void _dma_transfer(uint32_t *data, size_t length, int write) {
DMA_BCR(4) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16);
DMA_CHCR(4) = 0x01000200 | write;
+ return length;
+}
+
+static size_t _manual_write(const uint16_t *data, size_t length) {
+ if (length % 2)
+ _sdk_log("can't transfer a number of bytes that isn't multiple of 2\n");
+
+ length /= 2;
+
+ SPU_CTRL &= 0xffcf; // Disable DMA request
+ _wait_status(0x0030, 0x0000);
+
+ // Manual transfers have to be done by filling up the SPU's transfer buffer
+ // and then letting the SPU empty it one 64-byte chunk at a time.
+ uint16_t addr = _transfer_addr;
+
+ while (length) {
+ size_t chunk = _min(DMA_CHUNK_LENGTH * 2, length);
+ length -= chunk;
+
+ SPU_ADDR = addr;
+ addr += chunk / 4;
+
+ for (; chunk; chunk--)
+ SPU_DATA = *(data++);
+
+ SPU_CTRL |= 0x0010; // Manual transfer mode
+ _wait_status(0x0030, 0x0010);
+ _wait_status(0x0400, 0x0000);
+
+ // This additional delay is required according to nocash docs.
+ for (int i = 0; i < 1000; i++)
+ __asm__ volatile("");
+ }
+
+ return length;
}
/* Public API */
@@ -81,17 +118,16 @@ void SpuInit(void) {
DMA_DPCR |= 0x000b0000; // Enable DMA4
DMA_CHCR(4) = 0x00000201; // Stop DMA4
- SPU_CTRL = 0xc011; // Enable SPU, DAC, CD audio, set manual transfer mode
- _wait_status(0x001f, 0x0011);
+ SPU_DMA_CTRL = 0x0004; // Reset transfer mode
+ SPU_CTRL = 0xc001; // Enable SPU, DAC, CD audio, disable DMA request
+ _wait_status(0x003f, 0x0001);
- // Upload a dummy ADPCM block to the first 16 bytes of SPU RAM. This may be
- // freely used or overwritten.
- SPU_ADDR = WRITABLE_AREA_ADDR;
- _wait_status(0x0400, 0x0000);
+ // Upload a dummy looping ADPCM block to the first 16 bytes of SPU RAM.
+ // This may be freely used or overwritten.
+ uint32_t block[4] = { 0x0500, 0, 0, 0 };
- SPU_DATA = 0x0500;
- for (int i = 7; i; i--)
- SPU_DATA = 0x0000;
+ _transfer_addr = WRITABLE_AREA_ADDR;
+ _manual_write((const uint16_t *) block, 16);
// "Play" the dummy block on all channels. This will reset the start
// address and ADSR envelope status of each channel.
@@ -111,31 +147,26 @@ void SpuInit(void) {
SPU_CD_VOL_R = 0x7fff;
}
-void SpuRead(uint32_t *data, size_t size) {
- _dma_transfer(data, size, 0);
+size_t SpuRead(uint32_t *data, size_t size) {
+ return _dma_transfer(data, size, 0) * 4;
}
-void SpuWrite(const uint32_t *data, size_t size) {
+size_t SpuWrite(const uint32_t *data, size_t size) {
if (_transfer_addr < WRITABLE_AREA_ADDR)
- return;
+ return 0;
// I/O transfer mode is not that useful, but whatever.
- if (_transfer_mode) {
- SPU_ADDR = _transfer_addr;
- SPU_CTRL = (SPU_CTRL & 0xffcf) | 0x0010; // Manual transfer mode
- _wait_status(0x0400, 0x0000);
-
- for (int i = size; i; i -= 4) {
- uint32_t value = *(data++);
-
- SPU_DATA = (uint16_t) value;
- SPU_DATA = (uint16_t) (value >> 16);
- }
+ if (_transfer_mode)
+ return _manual_write((const uint16_t *) data, size) * 2;
+ else
+ return _dma_transfer((uint32_t *) data, size, 1) * 4;
+}
- return;
- }
+size_t SpuWritePartly(const uint32_t *data, size_t size) {
+ size_t _size = SpuWrite(data, size);
- _dma_transfer((uint32_t *) data, size, 1);
+ _transfer_addr += (_size + 1) / 2;
+ return _size;
}
SPU_TransferMode SpuSetTransferMode(SPU_TransferMode mode) {