shithub: psxe

Download patch

ref: 0e56d8b9679d592f9d22fbc4b84e452f5a783404
parent: 60550f8be5060a38225dd0a2ef0e920e48f249f4
author: allkern <lisandroaalarcon@gmail.com>
date: Fri Sep 29 16:03:46 EDT 2023

Initial SPU implementation

Fix GPU issue

--- a/frontend/main.c
+++ b/frontend/main.c
@@ -8,9 +8,17 @@
 #undef main
 
 void audio_update(void* ud, uint8_t* buf, int size) {
-    psx_cdrom_t* cdrom = ud;
+    psx_cdrom_t* cdrom = ((psx_t*)ud)->cdrom;
+    psx_spu_t* spu = ((psx_t*)ud)->spu;
 
     psx_cdrom_get_cdda_samples(cdrom, buf, size);
+
+    for (int i = 0; i < (size >> 2); i++) {
+        int16_t sample = psx_spu_get_sample(spu);
+
+        *(uint16_t*)(&buf[(i << 2) + 0]) = sample;
+        *(uint16_t*)(&buf[(i << 2) + 2]) = sample;
+    }
 }
 
 int main(int argc, const char* argv[]) {
@@ -45,7 +53,7 @@
     desired.channels = 2;
     desired.samples  = CD_SECTOR_SIZE >> 2;
     desired.callback = &audio_update;
-    desired.userdata = cdrom;
+    desired.userdata = psx;
 
     dev = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0);
 
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -329,20 +329,22 @@
     if (!CHCR_BUSY(spu))
         return;
 
-    // log_fatal("SPU DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x",
-    //     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)
-    // );
+    log_set_quiet(0);
+    log_fatal("SPU DMA transfer: madr=%08x, dir=%s, sync=%s (%u), step=%s, size=%x",
+        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)
+    );
 
-    // log_fatal("DICR: force=%u, en=%02x, irqen=%u, flags=%02x",
-    //     (dma->dicr >> 15) & 1,
-    //     (dma->dicr >> 16) & 0x7f,
-    //     (dma->dicr >> 23) & 1,
-    //     (dma->dicr >> 24) & 0x7f
-    // );
+    log_fatal("DICR: force=%u, en=%02x, irqen=%u, flags=%02x",
+        (dma->dicr >> 15) & 1,
+        (dma->dicr >> 16) & 0x7f,
+        (dma->dicr >> 23) & 1,
+        (dma->dicr >> 24) & 0x7f
+    );
+    log_set_quiet(1);
 
     uint32_t size = BCR_SIZE(spu) * BCR_BCNT(spu);
 
@@ -353,6 +355,17 @@
     }
 
     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);
+
+            psx_bus_write16(dma->bus, 0x1f801da8, data >> 16);
+            psx_bus_write16(dma->bus, 0x1f801da8, data & 0xffff);
+
+            dma->spu.madr += CHCR_STEP(spu) ? -4 : 4;
+        }
+    }
     
     // Clear BCR and CHCR trigger and busy bits
     dma->spu.chcr = 0;
--- a/psx/dev/gpu.c
+++ b/psx/dev/gpu.c
@@ -144,7 +144,7 @@
         // 4-bit
         case 0: {
             uint16_t texel = VRAM(tpx + (tx >> 2), tpy + ty);
-            uint16_t index = (texel >> ((tx & 0x1) << 3)) & 0xff;
+            uint16_t index = (texel >> ((tx & 0x3) << 2)) & 0xf;
 
             return VRAM(clutx + index, cluty);
         } break;
@@ -938,7 +938,7 @@
                 xpos = (gpu->xpos + gpu->xcnt) & 0x3ff;
             }
 
-            VRAM(xpos, ypos) = gpu->recv_data & 0xffff;
+            VRAM(xpos, ypos) = gpu->recv_data >> 16;
 
             ++gpu->xcnt;
             
@@ -1509,24 +1509,12 @@
 void psx_gpu_update_cmd(psx_gpu_t* gpu) {
     int type = (gpu->buf[0] >> 29) & 7;
 
-    if (type == 3) {
-        gpu_rect(gpu);
-
-        return;
-    }
-
-    if (type == 1) {
-        gpu_poly(gpu);
-
-        return;
-    }
-
     switch (type) {
-        case 1: gpu_poly(gpu); break;
-        case 3: gpu_rect(gpu); break;
-        case 4: gpu_copy(gpu); break;
-        case 5: gpu_recv(gpu); break;
-        case 6: gpu_send(gpu); break;
+        case 1: gpu_poly(gpu); return;
+        case 3: gpu_rect(gpu); return;
+        case 4: gpu_copy(gpu); return;
+        case 5: gpu_recv(gpu); return;
+        case 6: gpu_send(gpu); return;
         default: break;
     }
 
@@ -1566,8 +1554,8 @@
         // case 0x7e: gpu_cmd_7c(gpu); break;
         // case 0x7f: gpu_cmd_7c(gpu); break;
         // case 0x80: gpu_cmd_80(gpu); break;
-        case 0xa0: gpu_cmd_a0(gpu); break;
-        case 0xc0: gpu_cmd_c0(gpu); break;
+        // case 0xa0: gpu_cmd_a0(gpu); break;
+        // case 0xc0: gpu_cmd_c0(gpu); break;
         case 0xe1: {
             gpu->gpustat &= 0xfffff800;
             gpu->gpustat |= gpu->buf[0] & 0x7ff;
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -5,6 +5,73 @@
 #include "spu.h"
 #include "../log.h"
 
+static const int16_t g_psx_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,
+    0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,
+    0x0003, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, 0x0007,
+    0x0008, 0x0009, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,
+    0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0015, 0x0016, 0x0018,
+    0x0019, 0x001B, 0x001C, 0x001E, 0x0020, 0x0021, 0x0023, 0x0025,
+    0x0027, 0x0029, 0x002C, 0x002E, 0x0030, 0x0033, 0x0035, 0x0038,
+    0x003A, 0x003D, 0x0040, 0x0043, 0x0046, 0x0049, 0x004D, 0x0050,
+    0x0054, 0x0057, 0x005B, 0x005F, 0x0063, 0x0067, 0x006B, 0x006F,
+    0x0074, 0x0078, 0x007D, 0x0082, 0x0087, 0x008C, 0x0091, 0x0096,
+    0x009C, 0x00A1, 0x00A7, 0x00AD, 0x00B3, 0x00BA, 0x00C0, 0x00C7,
+    0x00CD, 0x00D4, 0x00DB, 0x00E3, 0x00EA, 0x00F2, 0x00FA, 0x0101,
+    0x010A, 0x0112, 0x011B, 0x0123, 0x012C, 0x0135, 0x013F, 0x0148,
+    0x0152, 0x015C, 0x0166, 0x0171, 0x017B, 0x0186, 0x0191, 0x019C,
+    0x01A8, 0x01B4, 0x01C0, 0x01CC, 0x01D9, 0x01E5, 0x01F2, 0x0200,
+    0x020D, 0x021B, 0x0229, 0x0237, 0x0246, 0x0255, 0x0264, 0x0273,
+    0x0283, 0x0293, 0x02A3, 0x02B4, 0x02C4, 0x02D6, 0x02E7, 0x02F9,
+    0x030B, 0x031D, 0x0330, 0x0343, 0x0356, 0x036A, 0x037E, 0x0392,
+    0x03A7, 0x03BC, 0x03D1, 0x03E7, 0x03FC, 0x0413, 0x042A, 0x0441,
+    0x0458, 0x0470, 0x0488, 0x04A0, 0x04B9, 0x04D2, 0x04EC, 0x0506,
+    0x0520, 0x053B, 0x0556, 0x0572, 0x058E, 0x05AA, 0x05C7, 0x05E4,
+    0x0601, 0x061F, 0x063E, 0x065C, 0x067C, 0x069B, 0x06BB, 0x06DC,
+    0x06FD, 0x071E, 0x0740, 0x0762, 0x0784, 0x07A7, 0x07CB, 0x07EF,
+    0x0813, 0x0838, 0x085D, 0x0883, 0x08A9, 0x08D0, 0x08F7, 0x091E,
+    0x0946, 0x096F, 0x0998, 0x09C1, 0x09EB, 0x0A16, 0x0A40, 0x0A6C,
+    0x0A98, 0x0AC4, 0x0AF1, 0x0B1E, 0x0B4C, 0x0B7A, 0x0BA9, 0x0BD8,
+    0x0C07, 0x0C38, 0x0C68, 0x0C99, 0x0CCB, 0x0CFD, 0x0D30, 0x0D63,
+    0x0D97, 0x0DCB, 0x0E00, 0x0E35, 0x0E6B, 0x0EA1, 0x0ED7, 0x0F0F,
+    0x0F46, 0x0F7F, 0x0FB7, 0x0FF1, 0x102A, 0x1065, 0x109F, 0x10DB,
+    0x1116, 0x1153, 0x118F, 0x11CD, 0x120B, 0x1249, 0x1288, 0x12C7,
+    0x1307, 0x1347, 0x1388, 0x13C9, 0x140B, 0x144D, 0x1490, 0x14D4,
+    0x1517, 0x155C, 0x15A0, 0x15E6, 0x162C, 0x1672, 0x16B9, 0x1700,
+    0x1747, 0x1790, 0x17D8, 0x1821, 0x186B, 0x18B5, 0x1900, 0x194B,
+    0x1996, 0x19E2, 0x1A2E, 0x1A7B, 0x1AC8, 0x1B16, 0x1B64, 0x1BB3,
+    0x1C02, 0x1C51, 0x1CA1, 0x1CF1, 0x1D42, 0x1D93, 0x1DE5, 0x1E37,
+    0x1E89, 0x1EDC, 0x1F2F, 0x1F82, 0x1FD6, 0x202A, 0x207F, 0x20D4,
+    0x2129, 0x217F, 0x21D5, 0x222C, 0x2282, 0x22DA, 0x2331, 0x2389,
+    0x23E1, 0x2439, 0x2492, 0x24EB, 0x2545, 0x259E, 0x25F8, 0x2653,
+    0x26AD, 0x2708, 0x2763, 0x27BE, 0x281A, 0x2876, 0x28D2, 0x292E,
+    0x298B, 0x29E7, 0x2A44, 0x2AA1, 0x2AFF, 0x2B5C, 0x2BBA, 0x2C18,
+    0x2C76, 0x2CD4, 0x2D33, 0x2D91, 0x2DF0, 0x2E4F, 0x2EAE, 0x2F0D,
+    0x2F6C, 0x2FCC, 0x302B, 0x308B, 0x30EA, 0x314A, 0x31AA, 0x3209,
+    0x3269, 0x32C9, 0x3329, 0x3389, 0x33E9, 0x3449, 0x34A9, 0x3509,
+    0x3569, 0x35C9, 0x3629, 0x3689, 0x36E8, 0x3748, 0x37A8, 0x3807,
+    0x3867, 0x38C6, 0x3926, 0x3985, 0x39E4, 0x3A43, 0x3AA2, 0x3B00,
+    0x3B5F, 0x3BBD, 0x3C1B, 0x3C79, 0x3CD7, 0x3D35, 0x3D92, 0x3DEF,
+    0x3E4C, 0x3EA9, 0x3F05, 0x3F62, 0x3FBD, 0x4019, 0x4074, 0x40D0,
+    0x412A, 0x4185, 0x41DF, 0x4239, 0x4292, 0x42EB, 0x4344, 0x439C,
+    0x43F4, 0x444C, 0x44A3, 0x44FA, 0x4550, 0x45A6, 0x45FC, 0x4651,
+    0x46A6, 0x46FA, 0x474E, 0x47A1, 0x47F4, 0x4846, 0x4898, 0x48E9,
+    0x493A, 0x498A, 0x49D9, 0x4A29, 0x4A77, 0x4AC5, 0x4B13, 0x4B5F,
+    0x4BAC, 0x4BF7, 0x4C42, 0x4C8D, 0x4CD7, 0x4D20, 0x4D68, 0x4DB0,
+    0x4DF7, 0x4E3E, 0x4E84, 0x4EC9, 0x4F0E, 0x4F52, 0x4F95, 0x4FD7,
+    0x5019, 0x505A, 0x509A, 0x50DA, 0x5118, 0x5156, 0x5194, 0x51D0,
+    0x520C, 0x5247, 0x5281, 0x52BA, 0x52F3, 0x532A, 0x5361, 0x5397,
+    0x53CC, 0x5401, 0x5434, 0x5467, 0x5499, 0x54CA, 0x54FA, 0x5529,
+    0x5558, 0x5585, 0x55B2, 0x55DE, 0x5609, 0x5632, 0x565B, 0x5684,
+    0x56AB, 0x56D1, 0x56F6, 0x571B, 0x573E, 0x5761, 0x5782, 0x57A3,
+    0x57C3, 0x57E2, 0x57FF, 0x581C, 0x5838, 0x5853, 0x586D, 0x5886,
+    0x589E, 0x58B5, 0x58CB, 0x58E0, 0x58F4, 0x5907, 0x5919, 0x592A,
+    0x593A, 0x5949, 0x5958, 0x5965, 0x5971, 0x597C, 0x5986, 0x598F,
+    0x5997, 0x599E, 0x59A4, 0x59A9, 0x59AD, 0x59B0, 0x59B2, 0x59B3
+};
+
 psx_spu_t* psx_spu_create() {
     return (psx_spu_t*)malloc(sizeof(psx_spu_t));
 }
@@ -15,15 +82,22 @@
     spu->io_base = PSX_SPU_BEGIN;
     spu->io_size = PSX_SPU_SIZE;
 
-    spu->ram = (uint8_t*)malloc(PSX_SPU_RAM_SIZE);
+    spu->ram = (uint8_t*)malloc(SPU_RAM_SIZE);
+
+    // Mute all voices
+    spu->endx = 0x00ffffff;
 }
 
 uint32_t psx_spu_read32(psx_spu_t* spu, uint32_t offset) {
-    return *(uint32_t*)(&spu->r[offset]);
+    const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+
+    return *((uint32_t*)(ptr + offset));
 }
 
 uint16_t psx_spu_read16(psx_spu_t* spu, uint32_t offset) {
-    return *(uint16_t*)(&spu->r[offset]);
+    const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+
+    return *((uint16_t*)(ptr + offset));
 }
 
 uint8_t psx_spu_read8(psx_spu_t* spu, uint32_t offset) {
@@ -33,13 +107,86 @@
 }
 
 void psx_spu_write32(psx_spu_t* spu, uint32_t offset, uint32_t value) {
-    *(uint32_t*)(&spu->r[offset]) = value;
+    const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+
+    *((uint32_t*)(ptr + offset)) = value;
 }
 
+
+void spu_read_block(psx_spu_t* spu, int v) {
+    spu->data[v].block_flags = spu->ram[spu->data[v].current_addr + 1];
+}
+
 void psx_spu_write16(psx_spu_t* spu, uint32_t offset, uint16_t value) {
-    *(uint16_t*)(&spu->r[offset]) = value;
-}
+    // Handle special cases first
+    switch (offset) {
+        case SPUR_KON: {
+            log_set_quiet(0);
+            for (int i = 0; i < 24; i++) {
+                if (value & (1 << i)) {
+                    log_fatal("KON voice %2u s=%06x r=%06x p=%04x",
+                        i,
+                        spu->voice[i].adsaddr,
+                        spu->voice[i].adraddr,
+                        spu->voice[i].adsampr
+                    );
+
+                    spu->data[i].current_addr = spu->voice[i].adsaddr;
+
+                    spu_read_block(spu, i);
+                }
+            }
+                    
+            log_set_quiet(1);
+            spu->endx &= ~(value & 0x00ffffff);
+        } return;
 
+        case SPUR_KOFF: {
+            spu->endx |= value & 0x00ffffff;
+        } return;
+
+        case SPUR_TADDR: {
+            spu->ramdta = value;
+            spu->taddr = value << 3;
+
+            // log_set_quiet(0);
+            // log_fatal("ramdta=%04x taddr=%08x", spu->ramdta, spu->taddr);
+            // log_set_quiet(1);
+        } return;
+
+        case SPUR_TFIFO: {
+            // log_set_quiet(0);
+            // log_fatal("TFIFO write %04x (index=%u)", value, spu->tfifo_index);
+            // log_set_quiet(1);
+            spu->ramdtf = value;
+            spu->tfifo[spu->tfifo_index++] = value;
+        } return;
+
+        case SPUR_SPUCNT: {
+            spu->spucnt = value;
+            spu->spustat &= 0xffc0;
+            spu->spustat |= value & 0x3f;
+
+            if ((value >> 4) & 3) {
+                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;
+                }
+                    
+                // log_set_quiet(0);
+                // log_fatal("Transfer start mode=%u size=%u", (value >> 4) & 3, spu->tfifo_index);
+                // log_set_quiet(1);
+
+                spu->tfifo_index = 0;
+            }
+        } break;
+    }
+
+    const uint8_t* ptr = (uint8_t*)&spu->voice[0].volumel;
+
+    *((uint16_t*)(ptr + offset)) = value;
+}
+
 void psx_spu_write8(psx_spu_t* spu, uint32_t offset, uint8_t value) {
     log_fatal("Unhandled 8-bit SPU write at offset %08x (%02x)", offset, value);
 }
@@ -47,4 +194,95 @@
 void psx_spu_destroy(psx_spu_t* spu) {
     free(spu->ram);
     free(spu);
+}
+
+/*
+  0-3   Shift  (0..12) (0=Loudest) (13..15=Reserved/Same as 9)
+  4-5   Filter (0..3) (only four filters, unlike SPU-ADPCM which has five)
+  6-7   Unused (should be 0)
+*/
+
+int16_t psx_spu_get_sample(psx_spu_t* spu) {
+    if (spu->endx == 0x00ffffff)
+        return 0x0000;
+
+    int voice_count = 0;
+    int output = 0x0000;
+
+    for (int v = 0; v < 24; v++) {
+        if (spu->endx & (1 << v))
+            continue;
+        
+        ++voice_count;
+
+        // Shift 3 older samples around
+        spu->data[v].s[3] = spu->data[v].s[2];
+        spu->data[v].s[2] = spu->data[v].s[1];
+        spu->data[v].s[1] = spu->data[v].s[0];
+
+        uint32_t sample_index = spu->data[v].counter >> 12;
+
+        if (sample_index >= 28) {
+            sample_index -= 28;
+
+            spu->data[v].counter &= 0xfff;
+            spu->data[v].counter |= sample_index << 12;
+
+            switch (spu->data[v].block_flags & 3) {
+                case 0: case 2: {
+                    spu->data[v].current_addr += 2;
+                } break;
+
+                case 1: {
+                    spu->data[v].current_addr = spu->data[v].repeat_addr;
+                    spu->endx |= (1 << v);
+                } break;
+
+                case 3: {
+                    spu->data[v].current_addr = spu->data[v].repeat_addr;
+                } break;
+            }
+
+            if (spu->data[v].block_flags & 4)
+                spu->data[v].repeat_addr = spu->data[v].current_addr;
+
+            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)];
+
+        // "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 out;
+
+        out  = (g0 * spu->data[v].s[3]) >> 15;
+        out += (g1 * spu->data[v].s[2]) >> 15;
+        out += (g2 * spu->data[v].s[1]) >> 15;
+        out += (g3 * spu->data[v].s[0]) >> 15;
+
+        output += out;
+
+        uint16_t step = spu->voice[0].adsampr;
+
+        /* To-do: Do pitch modulation here */
+
+        spu->data[v].counter += step;
+    }
+
+    return output / voice_count;
 }
\ No newline at end of file
--- a/psx/dev/spu.h
+++ b/psx/dev/spu.h
@@ -7,31 +7,110 @@
 #define PSX_SPU_SIZE  0x400
 #define PSX_SPU_END   0x1f801fff
 
-#define PSX_SPU_RAM_SIZE 0x80000
+#define SPU_RAM_SIZE 0x80000
 
-typedef struct {
+/*
+    1F801D88h - Voice 0..23 Key ON (Start Attack/Decay/Sustain) (KON) (W)
+    1F801D8Ch - Voice 0..23 Key OFF (Start Release) (KOFF) (W)
+    1F801D9Ch - Voice 0..23 ON/OFF (status) (ENDX) (R)
+    1F801DA6h - Sound RAM Data Transfer Address
+    1F801DA8h - Sound RAM Data Transfer Fifo
+    1F801DACh - Sound RAM Data Transfer Control (should be 0004h)
+*/
+
+#define SPUR_KON     0x188
+#define SPUR_KOFF    0x18c
+#define SPUR_ENDX    0x19c
+#define SPUR_TADDR   0x1a6
+#define SPUR_TFIFO   0x1a8
+#define SPUR_SPUCNT  0x1aa
+#define SPUR_TCTRL   0x1ac
+#define SPUR_SPUSTAT 0x1ae
+
+typedef struct __attribute__((__packed__)) {
     uint32_t io_base, io_size;
 
     uint8_t* ram;
 
-    uint8_t r[0x400];
+    struct {
+        uint16_t volumel;
+        uint16_t volumer;
+        uint16_t adsampr;
+        uint16_t adsaddr;
+        uint16_t envctl1;
+        uint16_t envctl2;
+        uint16_t envcvol;
+        uint16_t adraddr;
+    } voice[24];
 
-    uint32_t current_addr;
+    uint16_t mainlvol;
+    uint16_t mainrvol;
+    uint16_t revblvol;
+    uint16_t revbrvol;
+    uint32_t kon;
+    uint32_t koff;
+    uint32_t pmon;
+    uint32_t non;
+    uint32_t eon;
+    uint32_t endx;
+    uint16_t unk_da0;
+    uint16_t revbaddr;
+    uint16_t irq9addr;
+    uint16_t ramdta;
+    uint16_t ramdtf;
+    uint16_t spucnt;
+    uint16_t ramdtc;
+    uint16_t spustat;
+    uint32_t cdaivol;
+    uint32_t extivol;
+    uint32_t currvol;
+    uint32_t unk_dbc;
+    uint16_t dapf1;
+    uint16_t dapf2;
+    uint16_t viir;
+    uint16_t vcomb1;
+    uint16_t vcomb2;
+    uint16_t vcomb3;
+    uint16_t vcomb4;
+    uint16_t vwall;
+    uint16_t vapf1;
+    uint16_t vapf2;
+    uint16_t mlsame;
+    uint16_t mrsame;
+    uint16_t mlcomb1;
+    uint16_t mrcomb1;
+    uint16_t mlcomb2;
+    uint16_t mrcomb2;
+    uint16_t dlsame;
+    uint16_t drsame;
+    uint16_t mldiff;
+    uint16_t mrdiff;
+    uint16_t mlcomb3;
+    uint16_t mrcomb3;
+    uint16_t mlcomb4;
+    uint16_t mrcomb4;
+    uint16_t dldiff;
+    uint16_t drdiff;
+    uint16_t mlapf1;
+    uint16_t mrapf1;
+    uint16_t mlapf2;
+    uint16_t mrapf2;
+    uint16_t vlin;
+    uint16_t vrin;
 
-    // struct {
-    //     uint16_t volumel;
-    //     uint16_t volumer;
-    //     uint16_t adsampr;
-    //     uint16_t adsaddr;
-    //     uint16_t envctl1;
-    //     uint16_t envctl2;
-    //     uint16_t envcvol;
-    //     uint16_t adraddr;
-    // } voice[24];
+    // Internal registers unimplemented
 
-    // uint16_t mainvol[2];
-    // uint16_t echovol[2];
-    // uint32_t flags[3];
+    uint32_t taddr;
+    uint16_t tfifo[32];
+    uint16_t tfifo_index;
+
+    struct {
+        uint32_t counter;
+        uint32_t current_addr;
+        uint32_t repeat_addr;
+        int16_t s[4];
+        int block_flags;
+    } data[24];
 } psx_spu_t;
 
 psx_spu_t* psx_spu_create();
@@ -43,5 +122,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*);
 
 #endif
\ No newline at end of file
--