shithub: psxe

Download patch

ref: a8debfc9fa0201aabbb6a9c84d98d26ad579a999
parent: 430ffb36d2fb74b1d9163a676fad3ffaa544770e
author: allkern <lisandroaalarcon@gmail.com>
date: Sat Oct 7 15:55:49 EDT 2023

Improve MDEC

--- a/frontend/main.c
+++ b/frontend/main.c
@@ -80,6 +80,7 @@
 
     psx_pad_attach_joy(psx->pad, 0, input);
     psx_pad_attach_mcd(psx->pad, 0, "slot1.mcd");
+    psx_pad_attach_mcd(psx->pad, 1, "slot2.mcd");
 
     if (cfg->exe) {
         while (psx->cpu->pc != 0x80030000) {
--- a/psx/dev/dma.c
+++ b/psx/dev/dma.c
@@ -167,14 +167,18 @@
     if (!CHCR_BUSY(mdec_in))
         return;
 
-    for (int i = 0; i < BCR_SIZE(mdec_in); i++) {
-        uint32_t data = psx_bus_read32(dma->bus, dma->gpu.madr);
+    size_t size = BCR_SIZE(mdec_in) * BCR_BCNT(mdec_in);
 
+    for (int i = 0; i < size; i++) {
+        uint32_t data = psx_bus_read32(dma->bus, dma->mdec_in.madr);
+
         psx_bus_write32(dma->bus, 0x1f801820, data);
 
-        dma->gpu.madr += CHCR_STEP(mdec_in) ? -4 : 4;
+        dma->mdec_in.madr += CHCR_STEP(mdec_in) ? -4 : 4;
     }
 
+    dma->mdec_in_irq_delay = 1;
+
     dma->mdec_in.chcr &= ~(CHCR_BUSY_MASK | CHCR_TRIG_MASK);
     dma->mdec_in.bcr = 0;
 }
@@ -183,7 +187,20 @@
     if (!CHCR_BUSY(mdec_out))
         return;
 
-    log_fatal("MDEC_OUT DMA channel unimplemented");
+    size_t size = BCR_SIZE(mdec_out) * BCR_BCNT(mdec_out);
+
+    for (int i = 0; i < size; i++) {
+        uint32_t data = psx_bus_read32(dma->bus, 0x1f801820);
+
+        psx_bus_write32(dma->bus, dma->mdec_out.madr, data);
+
+        dma->mdec_out.madr += CHCR_STEP(mdec_out) ? -4 : 4;
+    }
+
+    dma->mdec_out_irq_delay = 1;
+
+    dma->mdec_out.chcr &= ~(CHCR_BUSY_MASK | CHCR_TRIG_MASK);
+    dma->mdec_out.bcr = 0;
 }
 
 void psx_dma_do_gpu_linked(psx_dma_t* dma) {
@@ -438,6 +455,20 @@
             dma->dicr |= DICR_DMA6FL;
 
         dma->otc_irq_delay = 0;
+    }
+
+    if (dma->mdec_in_irq_delay) {
+        if (dma->dicr & DICR_DMA0EN)
+            dma->dicr |= DICR_DMA0FL;
+        
+        dma->mdec_in_irq_delay = 0;
+    }
+
+    if (dma->mdec_out_irq_delay) {
+        if (dma->dicr & DICR_DMA1EN)
+            dma->dicr |= DICR_DMA1FL;
+        
+        dma->mdec_out_irq_delay = 0;
     }
 
     int prev_irq_signal = (dma->dicr & DICR_IRQSI) != 0;
--- a/psx/dev/dma.h
+++ b/psx/dev/dma.h
@@ -30,6 +30,8 @@
     dma_channel_t pio;
     dma_channel_t otc;
 
+    int mdec_in_irq_delay;
+    int mdec_out_irq_delay;
     int cdrom_irq_delay;
     int spu_irq_delay;
     int gpu_irq_delay;
--- a/psx/dev/mdec.c
+++ b/psx/dev/mdec.c
@@ -5,22 +5,213 @@
 #include <string.h>
 #include <stdlib.h>
 
+int zigzag[] = {
+    0 , 1 , 5 , 6 , 14, 15, 27, 28,
+    2 , 4 , 7 , 13, 16, 26, 29, 42,
+    3 , 8 , 12, 17, 25, 30, 41, 43,
+    9 , 11, 18, 24, 31, 40, 44, 53,
+    10, 19, 23, 32, 39, 45, 52, 54,
+    20, 22, 33, 38, 46, 51, 55, 60,
+    21, 34, 37, 47, 50, 56, 59, 61,
+    35, 36, 48, 49, 57, 58, 62, 63
+};
+
+int zagzig[] = {
+     0,  1,  8, 16,  9,  2,  3, 10,
+    17, 24, 32, 25, 18, 11,  4,  5,
+    12, 19, 26, 33, 40, 48, 41, 34,
+    27, 20, 13,  6,  7, 14, 21, 28,
+    35, 42, 49, 56, 57, 50, 43, 36, 
+    29, 22, 15, 23, 30, 37, 44, 51,
+    58, 59, 52, 45, 38, 31, 39, 46,
+    53, 60, 61, 54, 47, 55, 62, 63
+};
+
+float scalezag[] = {
+    0.125000, 0.173380, 0.173380, 0.163320, 0.240485, 0.163320, 0.146984, 0.226532,
+    0.226532, 0.146984, 0.125000, 0.203873, 0.213388, 0.203873, 0.125000, 0.098212,
+    0.173380, 0.192044, 0.192044, 0.173380, 0.098212, 0.067650, 0.136224, 0.163320,
+    0.172835, 0.163320, 0.136224, 0.067650, 0.034487, 0.093833, 0.128320, 0.146984,
+    0.146984, 0.128320, 0.093833, 0.034487, 0.047835, 0.088388, 0.115485, 0.125000,
+    0.115485, 0.088388, 0.047835, 0.045060, 0.079547, 0.098212, 0.098212, 0.079547,
+    0.045060, 0.040553, 0.067650, 0.077165, 0.067650, 0.040553, 0.034487, 0.053152, 
+    0.053152, 0.034487, 0.027097, 0.036612, 0.027097, 0.018664, 0.018664, 0.009515
+};
+
+#define EXTS10(v) (((int16_t)((v) << 6)) >> 6)
+#define CLAMP(v, l, h) ((v <= l) ? l : ((v >= h) ? h : v))
+#define MAX(a, b) (a > b ? a : b)
+
+void real_idct(int16_t* blk, int16_t* scale) {
+    int16_t buf[64];
+
+    int16_t* src = blk;
+    int16_t* dst = buf;
+
+    for (int pass = 0; pass < 2; pass++) {
+        for (int x = 0; x < 8; x++) {
+            for (int y = 0; y < 8; y++) {
+                int sum = 0;
+
+                for (int z = 0; z < 8; z++)
+                    sum += src[y+z*8] * (scale[x+z*8] / 8);
+                
+                dst[x+y*8] = (sum + 0xfff) / 0x2000;
+            }
+        }
+
+        int16_t* temp = src;
+
+        src = dst;
+        dst = temp;
+    }
+}
+
+#define IDCT_FUNC(blk, scale) real_idct(blk, scale)
+
+uint16_t* rl_decode_block(int16_t* blk, uint16_t* src, uint8_t* quant, int16_t* scale) {
+    int k = 0;
+
+    for (int i = 0; i < 64; i++)
+        blk[i] = 0;
+    
+    uint16_t n = *src;
+
+    src += 2;
+
+    while (n == 0xfe00) {
+        n = *src;
+
+        src += 2;
+    }
+
+    int q_scale = (n >> 10) & 0x3f;
+
+    int16_t val = EXTS10(n & 0x3ff) * quant[k];
+
+    while (k < 64) {
+        if (!q_scale)
+            val = EXTS10(n & 0x3ff) * 2;
+
+        val = CLAMP(val, -0x400, 0x3ff);
+        // val *= scalezag[k]; // For fast IDCT
+
+        if (q_scale > 0)
+            blk[zagzig[k]] = val;
+        
+        if (!q_scale)
+            blk[k] = val;
+
+        n = *src;
+
+        src += 2;
+
+        k += ((n >> 10) & 0x3f) + 1;
+
+        val = (EXTS10(n & 0x3ff) * quant[k] * q_scale + 4) / 8;
+    }
+
+    IDCT_FUNC(blk, scale);
+
+    return src;
+}
+
+void yuv_to_rgb(psx_mdec_t* mdec, int xx, int yy) {
+    for (int y = 0; y < 8; y++) {
+        for (int x = 0; x < 8; x++) {
+            int16_t r = mdec->crblk[((x + xx) >> 1) + ((y + yy) >> 1) * 8];
+            int16_t b = mdec->cbblk[((x + xx) >> 1) + ((y + yy) >> 1) * 8];
+            int16_t g = (-0.3437 * (float)b) + (-0.7143 * (float)r);
+
+            r = (1.402 * (float)r);
+            b = (1.772 * (float)b);
+
+            int16_t l = mdec->yblk[x + y * 8];
+
+            r = CLAMP(l + r, -128, 127);
+            g = CLAMP(l + g, -128, 127);
+            b = CLAMP(l + b, -128, 127);
+
+            if (!mdec->output_signed) {
+                r ^= 0x80;
+                g ^= 0x80;
+                b ^= 0x80;
+            }
+
+            if (mdec->output_depth == 3) {
+                uint16_t r5 = ((uint8_t)r) >> 3;
+                uint16_t g5 = ((uint8_t)g) >> 3;
+                uint16_t b5 = ((uint8_t)b) >> 3;
+
+                uint16_t rgb = (b << 10) | (g << 5) | r;
+
+                if (mdec->output_bit15)
+                    rgb |= 0x8000;
+                
+                mdec->output[0 + ((x + xx) + (y + yy) * 16) * 2] = rgb & 0xff;
+                mdec->output[1 + ((x + xx) + (y + yy) * 16) * 2] = rgb >> 8;
+            } else {
+                mdec->output[0 + ((x + xx) + (y + yy) * 16) * 3] = r & 0xff;
+                mdec->output[1 + ((x + xx) + (y + yy) * 16) * 3] = g & 0xff;
+                mdec->output[2 + ((x + xx) + (y + yy) * 16) * 3] = b & 0xff;
+            }
+        }
+    }
+}
+
 void mdec_nop(psx_mdec_t* mdec) { /* Do nothing */ }
 
 void mdec_decode_macroblock(psx_mdec_t* mdec) {
-    // To-do
+    if (mdec->output_depth < 2) {
+        rl_decode_block(mdec->yblk, mdec->input, mdec->y_quant_table, mdec->scale_table);
+
+        for (int i = 0; i < 64; i++) {
+            int16_t y = mdec->yblk[i] & 0xff;
+
+            if (mdec->output_depth == 1) {
+                mdec->output[i] = y;
+            } else {
+                // To-do
+                mdec->output[i] = 0;
+            }
+        }
+
+        mdec->output_words_remaining = ((mdec->output_depth == 1) ? 64 : 32) >> 2;
+        mdec->output_empty = 0;
+        mdec->output_index = 0;
+    } else {
+        uint16_t* ptr = mdec->input;
+
+        ptr = rl_decode_block(mdec->crblk, ptr, mdec->uv_quant_table, mdec->scale_table);
+        ptr = rl_decode_block(mdec->cbblk, ptr, mdec->uv_quant_table, mdec->scale_table);
+        ptr = rl_decode_block(mdec->yblk, ptr, mdec->y_quant_table, mdec->scale_table);
+        yuv_to_rgb(mdec, 0, 0);
+        ptr = rl_decode_block(mdec->yblk, ptr, mdec->y_quant_table, mdec->scale_table);
+        yuv_to_rgb(mdec, 0, 8);
+        ptr = rl_decode_block(mdec->yblk, ptr, mdec->y_quant_table, mdec->scale_table);
+        yuv_to_rgb(mdec, 8, 0);
+        ptr = rl_decode_block(mdec->yblk, ptr, mdec->y_quant_table, mdec->scale_table);
+        yuv_to_rgb(mdec, 8, 8);
+
+        mdec->output_words_remaining = ((mdec->output_depth == 3) ? 512 : 768) >> 2;
+        mdec->output_empty = 0;
+        mdec->output_index = 0;
+
+        log_set_quiet(0);
+        log_fatal("Finished decoding %u-bit MDEC data", (mdec->output_depth == 3) ? 15 : 24);
+        log_set_quiet(1);
+    }
 }
 
 void mdec_set_iqtab(psx_mdec_t* mdec) {
-    mdec->state = mdec->cmd & 1 ? MDEC_RECV_QUANT_COLOR : MDEC_RECV_QUANT;
-    mdec->data_remaining = mdec->cmd & 1 ? 32 : 16;
-    mdec->index = 0;
+    memcpy(mdec->y_quant_table, mdec->input, 64);
+
+    if (mdec->recv_color)
+        memcpy(mdec->uv_quant_table, &mdec->input[16], 64);
 }
 
 void mdec_set_scale(psx_mdec_t* mdec) {
-    mdec->state = MDEC_RECV_SCALE;
-    mdec->data_remaining = 32;
-    mdec->index = 0;
+    memcpy(mdec->scale_table, mdec->input, 128);
 }
 
 mdec_fn_t g_mdec_cmd_table[] = {
@@ -34,39 +225,6 @@
     mdec_nop
 };
 
-void mdec_recv_cmd(psx_mdec_t* mdec) {
-    log_fatal("MDEC command %u (%08x)", mdec->cmd >> 29, mdec->cmd);
-
-    g_mdec_cmd_table[mdec->cmd >> 29](mdec);
-}
-void mdec_recv_block(psx_mdec_t* mdec) {}
-void mdec_recv_quant(psx_mdec_t* mdec) {
-    mdec->data_remaining--;
-
-    if (!mdec->data_remaining)
-        mdec->state = MDEC_RECV_CMD;
-}
-void mdec_recv_quant_color(psx_mdec_t* mdec) {
-    mdec->data_remaining--;
-
-    if (!mdec->data_remaining)
-        mdec->state = MDEC_RECV_CMD;
-}
-void mdec_recv_scale(psx_mdec_t* mdec) {
-    mdec->data_remaining--;
-
-    if (!mdec->data_remaining)
-        mdec->state = MDEC_RECV_CMD;
-}
-
-mdec_fn_t g_mdec_recv_table[] = {
-    mdec_recv_cmd,
-    mdec_recv_block,
-    mdec_recv_quant,
-    mdec_recv_quant_color,
-    mdec_recv_scale
-};
-
 psx_mdec_t* psx_mdec_create() {
     return (psx_mdec_t*)malloc(sizeof(psx_mdec_t));
 }
@@ -83,9 +241,35 @@
 uint32_t psx_mdec_read32(psx_mdec_t* mdec, uint32_t offset) {
     switch (offset) {
         case 0: {
+            if (mdec->output_words_remaining) {
+                --mdec->output_words_remaining;
 
+                log_set_quiet(0);
+                log_fatal("output read %08x", 0);
+                log_set_quiet(1);
+
+                return ((uint32_t*)mdec->output)[mdec->output_index++];
+            } else {
+                mdec->output_empty = 1;
+                mdec->output_index = 0;
+            }
         } break;
-        case 4: return mdec->status;
+        case 4: {
+            uint32_t status = 0;
+
+            status |= mdec->words_remaining;
+            status |= mdec->current_block    << 16;
+            status |= mdec->output_bit15     << 23;
+            status |= mdec->output_signed    << 24;
+            status |= mdec->output_depth     << 25;
+            status |= mdec->output_request   << 27;
+            status |= mdec->input_request    << 28;
+            status |= mdec->busy             << 29;
+            status |= mdec->input_full       << 30;
+            status |= mdec->output_empty     << 31;
+
+            return 0x00000000;
+        } break;
     }
 
     return 0x0;
@@ -106,17 +290,97 @@
 void psx_mdec_write32(psx_mdec_t* mdec, uint32_t offset, uint32_t value) {
     switch (offset) {
         case 0: {
+            if (mdec->busy) {
+                mdec->input[mdec->input_index++] = value;
+
+                --mdec->words_remaining;
+
+                if (!mdec->words_remaining) {
+                    mdec->output_empty = 0;
+                    mdec->input_full = 1;
+                    mdec->busy = 0;
+
+                    g_mdec_cmd_table[mdec->cmd >> 29](mdec);
+
+                    free(mdec->input);
+                }
+
+                break;
+            }
+
             mdec->cmd = value;
+            mdec->output_empty = 1;
+            mdec->output_bit15 = (value >> 25) & 1;
+            mdec->output_signed = (value >> 26) & 1;
+            mdec->output_depth = (value >> 27) & 3;
+            mdec->input_index = 0;
+            mdec->busy = 1;
 
-            g_mdec_recv_table[mdec->state](mdec);
+            log_set_quiet(0);
+            switch (mdec->cmd >> 29) {
+                case MDEC_CMD_NOP: {
+                    mdec->busy = 0;
+                    mdec->words_remaining = 0;
+
+                    log_fatal("MDEC %08x: NOP", mdec->cmd);
+                } break;
+
+                case MDEC_CMD_DECODE: {
+                    mdec->words_remaining = mdec->cmd & 0xffff;
+
+                    log_fatal("MDEC %08x: decode macroblock %04x",
+                        mdec->cmd,
+                        mdec->words_remaining
+                    );
+                } break;
+
+                case MDEC_CMD_SET_QT: {
+                    mdec->recv_color = mdec->cmd & 1;
+                    mdec->words_remaining = mdec->recv_color ? 32 : 16;
+
+                    log_fatal("MDEC %08x: set quant tables %04x",
+                        mdec->cmd,
+                        mdec->words_remaining
+                    );
+                } break;
+
+                case MDEC_CMD_SET_ST: {
+                    mdec->words_remaining = 32;
+
+                    log_fatal("MDEC %08x: set scale table %04x",
+                        mdec->cmd,
+                        mdec->words_remaining
+                    );
+                } break;
+            }
+            log_set_quiet(1);
+
+            if (mdec->words_remaining) {
+                mdec->input_full = 0;
+                mdec->input_index = 0;
+                mdec->input = malloc(mdec->words_remaining * sizeof(uint32_t));
+            }
         } break;
 
         case 4: {
-            if (value & 0x80000000)
-                mdec->status = 0x80040000;
+            // Reset
+            if (value & 0x80000000) {
+                // status = 80040000h
+                mdec->busy            = 0;
+                mdec->words_remaining = 0;
+                mdec->output_bit15    = 0;
+                mdec->output_signed   = 0;
+                mdec->output_depth    = 0;
+                mdec->input_request   = 0;
+                mdec->output_request  = 0;
+                mdec->busy            = 0;
+                mdec->input_full      = 0;
+                mdec->output_empty    = 1;
+                mdec->current_block   = 4;
+            }
 
-            mdec->status &= 0xe7ffffff;
-            mdec->status |= (value & 0x60000000) >> 2;
+            mdec->output_request = (value & 0x20000000) != 0;
+            mdec->input_request = (value & 0x40000000) != 0;
         } break;
     }
 
@@ -137,4 +401,7 @@
 
 void psx_mdec_destroy(psx_mdec_t* mdec) {
     free(mdec);
-}
\ No newline at end of file
+}
+
+#undef CLAMP
+#undef MAX
\ No newline at end of file
--- a/psx/dev/mdec.h
+++ b/psx/dev/mdec.h
@@ -9,6 +9,9 @@
 #define PSX_MDEC_BEGIN   0x1f801820
 #define PSX_MDEC_END     0x1f801827
 
+#define MDEC_SCALE_TABLE_SIZE 64
+#define MDEC_QUANT_TABLE_SIZE 64
+
 enum {
     MDEC_RECV_CMD,
     MDEC_RECV_BLOCK,
@@ -17,6 +20,27 @@
     MDEC_RECV_SCALE
 };
 
+/*
+  31    Data-Out Fifo Empty (0=No, 1=Empty)
+  30    Data-In Fifo Full   (0=No, 1=Full, or Last word received)
+  29    Command Busy  (0=Ready, 1=Busy receiving or processing parameters)
+  28    Data-In Request  (set when DMA0 enabled and ready to receive data)
+  27    Data-Out Request (set when DMA1 enabled and ready to send data)
+  26-25 Data Output Depth  (0=4bit, 1=8bit, 2=24bit, 3=15bit)      ;CMD.28-27
+  24    Data Output Signed (0=Unsigned, 1=Signed)                  ;CMD.26
+  23    Data Output Bit15  (0=Clear, 1=Set) (for 15bit depth only) ;CMD.25
+  22-19 Not used (seems to be always zero)
+  18-16 Current Block (0..3=Y1..Y4, 4=Cr, 5=Cb) (or for mono: always 4=Y)
+  15-0  Number of Parameter Words remaining minus 1  (FFFFh=None)  ;CMD.Bit0-15
+*/
+
+enum {
+    MDEC_CMD_NOP,
+    MDEC_CMD_DECODE,
+    MDEC_CMD_SET_QT,
+    MDEC_CMD_SET_ST
+};
+
 typedef struct {
     uint32_t io_base, io_size;
 
@@ -25,6 +49,33 @@
     int state;
     int data_remaining;
     int index;
+
+    uint32_t* input;
+    int input_index;
+
+    uint8_t output[(16 * 16) * 3];
+    int output_index;
+    uint16_t output_words_remaining;
+
+    uint16_t words_remaining;
+    int current_block;
+    int output_bit15;
+    int output_signed;
+    int output_depth;
+    int input_request;
+    int output_request;
+    int busy;
+    int input_full;
+    int output_empty;
+
+    int recv_color;
+    uint8_t uv_quant_table[MDEC_QUANT_TABLE_SIZE];
+    uint8_t y_quant_table[MDEC_QUANT_TABLE_SIZE];
+    int16_t scale_table[MDEC_SCALE_TABLE_SIZE];
+
+    int16_t yblk[64];
+    int16_t crblk[64];
+    int16_t cbblk[64];
 
     uint32_t status;
 } psx_mdec_t;
--- a/psx/dev/pad.c
+++ b/psx/dev/pad.c
@@ -9,7 +9,7 @@
     psx_input_t* joy = pad->joy_slot[(pad->ctrl >> 13) & 1];
     psx_mcd_t* mcd = pad->mcd_slot[(pad->ctrl >> 13) & 1];
 
-    if ((!(joy || mcd)) || !pad->dest)
+    if (!pad->dest)
         return 0xffffffff;
 
     if (!(pad->ctrl & CTRL_JOUT) && !(pad->ctrl & CTRL_RXEN))
@@ -17,6 +17,12 @@
 
     switch (pad->dest) {
         case DEST_JOY: {
+            if (!joy) {
+                pad->dest = 0;
+
+                return 0xffffffff;
+            }
+
             uint8_t data = joy->read_func(joy->udata);
 
             if (!joy->query_fifo_func(joy->udata))
@@ -26,6 +32,12 @@
         } break;
 
         case DEST_MCD: {
+            if (!mcd) {
+                pad->dest = 0;
+
+                return 0xffffffff;
+            }
+
             uint8_t data = psx_mcd_read(mcd);
 
             if (!psx_mcd_query(mcd))
@@ -42,18 +54,25 @@
     psx_input_t* joy = pad->joy_slot[(pad->ctrl >> 13) & 1];
     psx_mcd_t* mcd = pad->mcd_slot[(pad->ctrl >> 13) & 1];
 
-    if ((!(joy || mcd)) || !(pad->ctrl & CTRL_TXEN))
+    if (!(pad->ctrl & CTRL_TXEN))
         return;
 
-    if (pad->ctrl & CTRL_ACIE)
-        pad->cycles_until_irq = 1500;
-
     if (!pad->dest) {
-        if ((data == DEST_JOY) || (data == DEST_MCD))
+        if ((data == DEST_JOY) || (data == DEST_MCD)) {
             pad->dest = data;
+        
+            if (pad->ctrl & CTRL_ACIE)
+                pad->cycles_until_irq = 1500;
+        }
     } else {
         switch (pad->dest) {
             case DEST_JOY: {
+                if (!joy) {
+                    pad->dest = 0;
+
+                    return;
+                }
+
                 joy->write_func(joy->udata, data);
 
                 if (!joy->query_fifo_func(joy->udata))
@@ -61,6 +80,12 @@
             } break;
 
             case DEST_MCD: {
+                if (!mcd) {
+                    pad->dest = 0;
+
+                    return;
+                }
+
                 psx_mcd_write(mcd, data);
 
                 if (!psx_mcd_query(mcd))
@@ -67,6 +92,9 @@
                     pad->dest = 0;
             } break;
         }
+
+        if (pad->ctrl & CTRL_ACIE)
+            pad->cycles_until_irq = 1500;
     }
 }
 
--- a/psx/dev/pad.h
+++ b/psx/dev/pad.h
@@ -7,6 +7,8 @@
 #include "input.h"
 #include "mcd.h"
 
+#include "../input/sda.h"
+
 #define PSX_PAD_BEGIN 0x1f801040
 #define PSX_PAD_SIZE  0x10
 #define PSX_PAD_END   0x1f80104f
@@ -93,6 +95,16 @@
 enum {
     DEST_JOY = 0x01,
     DEST_MCD = 0x81
+};
+
+enum {
+    PAD_CONTROLLER_SDA,
+    PAD_CONTROLLER_MOUSE,
+    PAD_CONTROLLER_NAMCO_VOLUME,
+    PAD_CONTROLLER_SANKYO_NASUKA,
+    PAD_WHEEL_NEGCON,
+    PAD_WHEEL_MADCATZ,
+    PAD_WHEEL_MADCATZ_MC2
 };
 
 /*
--- a/psx/dev/spu.c
+++ b/psx/dev/spu.c
@@ -524,4 +524,7 @@
     uint16_t clampr = CLAMP(right, INT16_MIN, INT16_MAX);
 
     return clampl | (((uint32_t)clampr) << 16);
-}
\ No newline at end of file
+}
+
+#undef CLAMP
+#undef MAX
\ No newline at end of file
--