shithub: psxe

Download patch

ref: 6057aff755d8ad6ef6a0b624053a44eceeff4aab
parent: 0e56d8b9679d592f9d22fbc4b84e452f5a783404
author: allkern <lisandroaalarcon@gmail.com>
date: Sat Sep 30 20:47:04 EDT 2023

Massively improve SPU emulation

--- a/frontend/main.c
+++ b/frontend/main.c
@@ -14,10 +14,10 @@
     psx_cdrom_get_cdda_samples(cdrom, buf, size);
 
     for (int i = 0; i < (size >> 2); i++) {
-        int16_t sample = psx_spu_get_sample(spu);
+        int32_t sample = psx_spu_get_sample(spu) / 2;
 
-        *(uint16_t*)(&buf[(i << 2) + 0]) = sample;
-        *(uint16_t*)(&buf[(i << 2) + 2]) = sample;
+        *(uint16_t*)(&buf[(i << 2) + 0]) += sample;
+        *(uint16_t*)(&buf[(i << 2) + 2]) += sample;
     }
 }
 
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -330,12 +330,12 @@
         return;
 
     log_set_quiet(0);
-    log_fatal("SPU DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x",
+    log_fatal("SPU DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x, blocks=%u",
         dma->spu.madr,
         CHCR_TDIR(spu) ? "to device" : "to RAM",
         g_psx_dma_sync_type_name_table[CHCR_SYNC(spu)], CHCR_SYNC(spu),
         CHCR_STEP(spu) ? "decrementing" : "incrementing",
-        BCR_SIZE(spu)
+        BCR_SIZE(spu), BCR_BCNT(spu)
     );
 
     log_fatal("DICR: force=%u, en=%02x, irqen=%u, flags=%02x",
@@ -346,7 +346,8 @@
     );
     log_set_quiet(1);
 
-    uint32_t size = BCR_SIZE(spu) * BCR_BCNT(spu);
+    uint32_t size = BCR_SIZE(spu);
+    uint32_t blocks = BCR_BCNT(spu);
 
     if (!size) {
         log_fatal("0 sized SPU DMA");
@@ -357,13 +358,15 @@
     dma->spu_irq_delay = 1;
 
     if (CHCR_TDIR(spu)) {
-        for (int i = 0; i < size; i++) {
-            uint32_t data = psx_bus_read32(dma->bus, dma->spu.madr);
+        for (int j = 0; j < blocks; j++) {
+            for (int i = 0; i < size; i++) {
+                uint32_t data = psx_bus_read32(dma->bus, dma->spu.madr);
 
-            psx_bus_write16(dma->bus, 0x1f801da8, data >> 16);
-            psx_bus_write16(dma->bus, 0x1f801da8, data & 0xffff);
+                psx_bus_write16(dma->bus, 0x1f801da8, data & 0xffff);
+                psx_bus_write16(dma->bus, 0x1f801da8, data >> 16);
 
-            dma->spu.madr += CHCR_STEP(spu) ? -4 : 4;
+                dma->spu.madr += CHCR_STEP(spu) ? -4 : 4;
+            }
         }
     }
     
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -1704,6 +1704,8 @@
         if (gpu->event_cb_table[GPU_EVENT_VBLANK_END])
             gpu->event_cb_table[GPU_EVENT_VBLANK_END](gpu);
 
+        // psx_ic_irq(gpu->ic, IC_SPU);
+
         gpu->line = 0;
     }
 }
--- a/psx/dev/mcd.c
+++ b/psx/dev/mcd.c
@@ -43,10 +43,6 @@
                 case 'S': mcd->state = MCD_S_STATE_TX_ACK1; break;
             }
 
-            /**/ log_set_quiet(0);
-            /**/ log_fatal("mcd read %02x", mcd->tx_data);
-            /**/ log_set_quiet(1);
-
             return mcd->tx_data;
         } break;
 
@@ -71,10 +67,6 @@
                 break;
             }
 
-            /**/ log_set_quiet(0);
-            /**/ log_fatal("mcd read %02x", data);
-            /**/ log_set_quiet(1);
-
             return data;
         } break;
         case MCD_R_STATE_TX_CHK: mcd->tx_data = mcd->checksum; break;
@@ -82,10 +74,6 @@
             mcd->tx_data_ready = 0;
             mcd->state = MCD_STATE_TX_HIZ;
 
-            /**/ log_set_quiet(0);
-            /**/ log_fatal("mcd read %02x", 'G');
-            /**/ log_set_quiet(1);
-
             return 'G';
         } break;
     }
@@ -93,18 +81,10 @@
     mcd->tx_data_ready = 1;
     mcd->state++;
 
-    /**/ log_set_quiet(0);
-    /**/ log_fatal("mcd read %02x", mcd->tx_data);
-    /**/ log_set_quiet(1);
-
     return mcd->tx_data;
 }
 
 void psx_mcd_write(psx_mcd_t* mcd, uint8_t data) {
-    /**/ log_set_quiet(0);
-    /**/ log_fatal("mcd write %02x", data);
-    /**/ log_set_quiet(1);
-
     switch (mcd->state) {
         case MCD_STATE_TX_FLG: mcd->mode = data; break;
         case MCD_R_STATE_RX_MSB: mcd->msb = data; break;
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -5,7 +5,15 @@
 #include "spu.h"
 #include "../log.h"
 
-static const int16_t g_psx_spu_gauss_table[] = {
+static const int g_spu_pos_adpcm_table[] = {
+    0, +60, +115, +98, +122
+};
+
+static const int g_spu_neg_adpcm_table[] = {
+    0,   0,  -52, -55,  -60
+};
+
+static const int16_t g_spu_gauss_table[] = {
     -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001,
     -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001,
     0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
@@ -84,6 +92,8 @@
 
     spu->ram = (uint8_t*)malloc(SPU_RAM_SIZE);
 
+    memset(spu->ram, 0, SPU_RAM_SIZE);
+
     // Mute all voices
     spu->endx = 0x00ffffff;
 }
@@ -106,62 +116,116 @@
     return 0x0;
 }
 
-void psx_spu_write32(psx_spu_t* spu, uint32_t offset, uint32_t value) {
-    const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+void spu_read_block(psx_spu_t* spu, int v) {
+    uint32_t addr = spu->data[v].current_addr;
+    uint8_t hdr = spu->ram[addr];
 
-    *((uint32_t*)(ptr + offset)) = value;
-}
+    spu->data[v].block_flags = spu->ram[addr + 1];
 
+    unsigned shift  = 12 - (hdr & 0x0f);
+    unsigned filter = (hdr & 0x30) >> 4;
 
-void spu_read_block(psx_spu_t* spu, int v) {
-    spu->data[v].block_flags = spu->ram[spu->data[v].current_addr + 1];
+    int32_t f0 = g_spu_pos_adpcm_table[filter];
+    int32_t f1 = g_spu_neg_adpcm_table[filter];
+
+    for (int j = 0; j < 28; j++) {
+        uint16_t n = (spu->ram[addr + 2 + (j >> 1)] >> ((j & 1) * 4)) & 0xf;
+
+        // Sign extend t
+        int16_t t = (int16_t)(n << 12) >> 12; 
+        int16_t s = (t << shift) + (((spu->data[v].h[0] * f0) + (spu->data[v].h[1] * f1) + 32) / 64);
+        
+        s = (s < INT16_MIN) ? INT16_MIN : ((s > INT16_MAX) ? INT16_MAX : s);
+
+        spu->data[v].h[1] = spu->data[v].h[0];
+        spu->data[v].h[0] = s;
+        spu->data[v].buf[j] = s;
+    }
 }
 
-void psx_spu_write16(psx_spu_t* spu, uint32_t offset, uint16_t value) {
-    // Handle special cases first
+int spu_handle_write(psx_spu_t* spu, uint32_t offset, uint32_t value) {
     switch (offset) {
         case SPUR_KON: {
-            log_set_quiet(0);
+            if (!value)
+                return 1;
+
+            // log_set_quiet(0);
+            log_fatal("KON %04x:", value);
             for (int i = 0; i < 24; i++) {
-                if (value & (1 << i)) {
-                    log_fatal("KON voice %2u s=%06x r=%06x p=%04x",
+                if ((value & (1 << i))) {
+                    spu->data[i].playing = 1;
+                    spu->data[i].current_addr = spu->voice[i].adsaddr << 3;
+                    spu->data[i].repeat_addr = spu->data[i].current_addr;
+                    spu->data[i].lvol = (float)(spu->voice[i].volumel << 1) / (float)0x7fff;
+                    spu->data[i].rvol = (float)(spu->voice[i].volumer << 1) / (float)0x7fff;
+
+                    log_fatal("    voice %2u s=%06x r=%06x p=%04x vol=%f,%f",
                         i,
-                        spu->voice[i].adsaddr,
-                        spu->voice[i].adraddr,
-                        spu->voice[i].adsampr
+                        spu->voice[i].adsaddr << 3,
+                        spu->voice[i].adraddr << 3,
+                        spu->voice[i].adsampr,
+                        spu->data[i].lvol,
+                        spu->data[i].rvol
                     );
 
-                    spu->data[i].current_addr = spu->voice[i].adsaddr;
-
                     spu_read_block(spu, i);
                 }
             }
                     
-            log_set_quiet(1);
+            // log_set_quiet(1);
             spu->endx &= ~(value & 0x00ffffff);
-        } return;
+        } return 1;
 
         case SPUR_KOFF: {
-            spu->endx |= value & 0x00ffffff;
-        } return;
+            // log_set_quiet(0);
+            log_fatal("KOFF %04x:", value);
+            for (int i = 0; i < 24; i++) {
+                if ((value & (1 << i))) {
+                    spu->data[i].playing = 0;
 
+                    log_fatal("    voice %2u",
+                        i,
+                        spu->voice[i].adsaddr << 3,
+                        spu->voice[i].adraddr << 3,
+                        spu->voice[i].adsampr,
+                        spu->data[i].lvol,
+                        spu->data[i].rvol
+                    );
+                }
+            }
+            // log_set_quiet(1);
+
+            //spu->endx |= value & 0x00ffffff;
+        } return 1;
+
         case SPUR_TADDR: {
             spu->ramdta = value;
             spu->taddr = value << 3;
 
-            // log_set_quiet(0);
+            // // log_set_quiet(0);
             // log_fatal("ramdta=%04x taddr=%08x", spu->ramdta, spu->taddr);
-            // log_set_quiet(1);
-        } return;
+            // // log_set_quiet(1);
+        } return 1;
 
         case SPUR_TFIFO: {
-            // log_set_quiet(0);
+            // // log_set_quiet(0);
             // log_fatal("TFIFO write %04x (index=%u)", value, spu->tfifo_index);
-            // log_set_quiet(1);
+            // // log_set_quiet(1);
             spu->ramdtf = value;
             spu->tfifo[spu->tfifo_index++] = value;
-        } return;
 
+            if (spu->tfifo_index == 32) {
+                if (((spu->spucnt >> 4) & 3) == 2) {
+                    for (int i = 0; i < spu->tfifo_index; i++) {
+                        spu->ram[spu->taddr++] = spu->tfifo[i] & 0xff;
+                        spu->ram[spu->taddr++] = spu->tfifo[i] >> 8;
+                    }
+
+                    spu->tfifo_index = 0;
+                }
+            }
+        } return 1;
+
         case SPUR_SPUCNT: {
             spu->spucnt = value;
             spu->spustat &= 0xffc0;
@@ -173,17 +237,35 @@
                     spu->ram[spu->taddr++] = spu->tfifo[i] >> 8;
                 }
                     
-                // log_set_quiet(0);
+                // // log_set_quiet(0);
                 // log_fatal("Transfer start mode=%u size=%u", (value >> 4) & 3, spu->tfifo_index);
-                // log_set_quiet(1);
+                // // log_set_quiet(1);
 
                 spu->tfifo_index = 0;
             }
-        } break;
+        } return 1;
     }
 
+    return 0;
+}
+
+void psx_spu_write32(psx_spu_t* spu, uint32_t offset, uint32_t value) {
+    // Handle special cases first
+    if (spu_handle_write(spu, offset, value))
+        return;
+
     const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
 
+    *((uint32_t*)(ptr + offset)) = value;
+}
+
+void psx_spu_write16(psx_spu_t* spu, uint32_t offset, uint16_t value) {
+    // Handle special cases first
+    if (spu_handle_write(spu, offset, value))
+        return;
+
+    const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+
     *((uint16_t*)(ptr + offset)) = value;
 }
 
@@ -202,7 +284,7 @@
   6-7   Unused (should be 0)
 */
 
-int16_t psx_spu_get_sample(psx_spu_t* spu) {
+int32_t psx_spu_get_sample(psx_spu_t* spu) {
     if (spu->endx == 0x00ffffff)
         return 0x0000;
 
@@ -210,7 +292,7 @@
     int output = 0x0000;
 
     for (int v = 0; v < 24; v++) {
-        if (spu->endx & (1 << v))
+        if (!spu->data[v].playing)
             continue;
         
         ++voice_count;
@@ -230,12 +312,13 @@
 
             switch (spu->data[v].block_flags & 3) {
                 case 0: case 2: {
-                    spu->data[v].current_addr += 2;
+                    spu->data[v].current_addr += 0x10;
                 } break;
 
                 case 1: {
                     spu->data[v].current_addr = spu->data[v].repeat_addr;
-                    spu->endx |= (1 << v);
+                    //spu->endx |= (1 << v);
+                    spu->data[v].playing = 0;
                 } break;
 
                 case 3: {
@@ -249,25 +332,15 @@
             spu_read_block(spu, v);
         }
 
-        uint8_t hdr = spu->ram[spu->data[v].current_addr];
-
         // Fetch ADPCM sample
-        uint16_t sample = spu->ram[spu->data[v].current_addr + 2 + (sample_index >> 1)];
+        spu->data[v].s[0] = spu->data[v].buf[sample_index];
 
-        // "Expand" sample (according to XA-ADPCM)
-        sample >>= (sample_index & 1) * 4;
-        sample &= 0xf;
-        sample <<= 12;
-        sample >>= hdr & 0xf;
-
-        spu->data[v].s[0] = sample;
-
         // Apply 4-point Gaussian interpolation
         uint8_t gauss_index = (spu->data[v].counter >> 4) & 0xff;
-        int16_t g0 = g_psx_spu_gauss_table[0x0ff - gauss_index];
-        int16_t g1 = g_psx_spu_gauss_table[0x1ff - gauss_index];
-        int16_t g2 = g_psx_spu_gauss_table[0x100 + gauss_index];
-        int16_t g3 = g_psx_spu_gauss_table[0x000 + gauss_index];
+        int16_t g0 = g_spu_gauss_table[0x0ff - gauss_index];
+        int16_t g1 = g_spu_gauss_table[0x1ff - gauss_index];
+        int16_t g2 = g_spu_gauss_table[0x100 + gauss_index];
+        int16_t g3 = g_spu_gauss_table[0x000 + gauss_index];
         int16_t out;
 
         out  = (g0 * spu->data[v].s[3]) >> 15;
@@ -275,9 +348,9 @@
         out += (g2 * spu->data[v].s[1]) >> 15;
         out += (g3 * spu->data[v].s[0]) >> 15;
 
-        output += out;
+        output += out * ((spu->data[v].lvol + spu->data[v].rvol) / 2);
 
-        uint16_t step = spu->voice[0].adsampr;
+        uint16_t step = spu->voice[v].adsampr;
 
         /* To-do: Do pitch modulation here */
 
@@ -284,5 +357,10 @@
         spu->data[v].counter += step;
     }
 
-    return output / voice_count;
+    if (!voice_count)
+        return 0x00000000;
+
+    output = (output < INT16_MIN) ? INT16_MIN : ((output > INT16_MAX) ? INT16_MAX : output);
+
+    return output;
 }
\ No newline at end of file
--- a/psx/dev/spu.h
+++ b/psx/dev/spu.h
@@ -105,11 +105,16 @@
     uint16_t tfifo_index;
 
     struct {
+        int playing;
         uint32_t counter;
         uint32_t current_addr;
         uint32_t repeat_addr;
         int16_t s[4];
         int block_flags;
+        int16_t buf[28];
+        int16_t h[2];
+        float lvol;
+        float rvol;
     } data[24];
 } psx_spu_t;
 
@@ -122,6 +127,6 @@
 void psx_spu_write16(psx_spu_t*, uint32_t, uint16_t);
 void psx_spu_write8(psx_spu_t*, uint32_t, uint8_t);
 void psx_spu_destroy(psx_spu_t*);
-int16_t psx_spu_get_sample(psx_spu_t*);
+int32_t psx_spu_get_sample(psx_spu_t*);
 
 #endif
\ No newline at end of file
--