shithub: psxe

Download patch

ref: 4787a909b713b7f655172177fc8ea509cc1b2f17
parent: 71654b54031465d6eef062cacf718975f24fe668
author: allkern <lisandroaalarcon@gmail.com>
date: Thu Jul 6 18:07:08 EDT 2023

Properly implemented input

--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,7 @@
 
 SOURCES := $(wildcard psx/*.c)
 SOURCES += $(wildcard psx/dev/*.c)
+SOURCES += $(wildcard psx/input/*.c)
 SOURCES += $(wildcard frontend/*.c)
 
 bin/psxe frontend/main.c:
--- a/build-win32.ps1
+++ b/build-win32.ps1
@@ -14,6 +14,7 @@
     -I"`"$($SDL2_DIR)\include\SDL2`"" `
     "psx\*.c" `
     "psx\dev\*.c" `
+    "psx\input\*.c" `
     "frontend\*.c" `
     -o "bin\psxe.exe" `
     -DREP_VERSION="`"$($VERSION_TAG)`"" `
--- a/build-win64.ps1
+++ b/build-win64.ps1
@@ -14,6 +14,7 @@
     -I"`"$($SDL2_DIR)\include\SDL2`"" `
     "psx\*.c" `
     "psx\dev\*.c" `
+    "psx\input\*.c" `
     "frontend\*.c" `
     -o "bin\psxe.exe" `
     -DREP_VERSION="`"$($VERSION_TAG)`"" `
--- a/frontend/main.c
+++ b/frontend/main.c
@@ -1,4 +1,5 @@
 #include "psx/psx.h"
+#include "psx/input/sda.h"
 
 #include "screen.h"
 #include "config.h"
@@ -17,17 +18,29 @@
     psx_t* psx = psx_create();
     psx_init(psx, cfg->bios);
 
-    psx_gpu_t* gpu = psx_get_gpu(psx);
-
     psxe_screen_t* screen = psxe_screen_create();
     psxe_screen_init(screen, psx);
     psxe_screen_set_scale(screen, 2);
     psxe_screen_reload(screen);
 
+    psx_gpu_t* gpu = psx_get_gpu(psx);
+
     psx_gpu_set_event_callback(gpu, GPU_EVENT_DMODE, psxe_gpu_dmode_event_cb);
     psx_gpu_set_event_callback(gpu, GPU_EVENT_VBLANK, psxe_gpu_vblank_event_cb);
     psx_gpu_set_udata(gpu, 0, screen);
 
+    psxi_sda_t* controller = psxi_sda_create();
+
+    psxi_sda_init(controller, SDA_MODEL_DIGITAL);
+
+    psx_input_t* input = psx_input_create();
+
+    psx_input_init(input);
+
+    psxi_sda_init_input(controller, input);
+
+    psx_pad_init_slot(psx->pad, 0, input);
+
     if (cfg->exe) {
         while (psx->cpu->pc != 0x80030000) {
             psx_update(psx);
@@ -54,7 +67,9 @@
     log_fatal("gp=%08x sp=%08x fp=%08x ra=%08x", cpu->r[28], cpu->r[29], cpu->r[30], cpu->r[31]);
     log_fatal("pc=%08x hi=%08x lo=%08x ep=%08x", cpu->pc, cpu->hi, cpu->lo, cpu->cop0_r[COP0_EPC]);
 
+    psx_input_destroy(input);
     psx_destroy(psx);
+    psxi_sda_destroy(controller);
     psxe_screen_destroy(screen);
 
     return 0;
--- a/frontend/screen.c
+++ b/frontend/screen.c
@@ -120,13 +120,13 @@
 
                 uint16_t mask = screen_get_button(event.key.keysym.sym);
 
-                psx_pad_button_press(screen->pad, mask);
+                psx_pad_button_press(screen->pad, 0, mask);
             } break;
 
             case SDL_KEYUP: {
                 uint16_t mask = screen_get_button(event.key.keysym.sym);
 
-                psx_pad_button_release(screen->pad, mask);
+                psx_pad_button_release(screen->pad, 0, mask);
             } break;
         }
     }
--- a/psx/dev/ic.h
+++ b/psx/dev/ic.h
@@ -32,7 +32,7 @@
     IC_TMR0         = 0x010,
     IC_TMR1         = 0x020,
     IC_TMR2         = 0x040,
-    IC_CTRL         = 0x080,
+    IC_JOY          = 0x080,
     IC_SIO          = 0x100,
     IC_SPU          = 0x200,
     IC_LP_PIO       = 0x400
--- /dev/null
+++ b/psx/dev/input.c
@@ -1,0 +1,41 @@
+#ifndef PAD_H
+#define PAD_H
+
+#include "input.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+psx_input_t* psx_input_create() {
+    return (psx_input_t*)malloc(sizeof(psx_input_t));
+}
+
+void psx_input_init(psx_input_t* input) {
+    memset(input, 0, sizeof(psx_input_t));
+}
+
+void psx_input_set_write_func(psx_input_t* input, psx_input_write_t write_func) {
+    input->write_func = write_func;
+}
+
+void psx_input_set_read_func(psx_input_t* input, psx_input_read_t read_func) {
+    input->read_func = read_func;
+}
+
+void psx_input_set_on_button_press_func(psx_input_t* input, psx_input_on_button_press_t on_button_press_func) {
+    input->on_button_press_func = on_button_press_func;
+}
+
+void psx_input_set_on_button_release_func(psx_input_t* input, psx_input_on_button_release_t on_button_release_func) {
+    input->on_button_release_func = on_button_release_func;
+}
+
+void psx_input_set_on_analog_change_func(psx_input_t* input, psx_input_on_analog_change_t on_analog_change_func) {
+    input->on_analog_change_func = on_analog_change_func;
+}
+
+void psx_input_destroy(psx_input_t* input) {
+    free(input);
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+++ b/psx/dev/input.h
@@ -1,0 +1,38 @@
+#ifndef INPUT_H
+#define INPUT_H
+
+#include <stdint.h>
+
+struct psx_input_t;
+
+typedef struct psx_input_t psx_input_t;
+
+typedef void (*psx_input_write_t)(void*, uint16_t);
+typedef uint32_t (*psx_input_read_t)(void*);
+typedef void (*psx_input_on_button_press_t)(void*, uint16_t);
+typedef void (*psx_input_on_button_release_t)(void*, uint16_t);
+typedef void (*psx_input_on_analog_change_t)(void*, uint16_t);
+typedef int (*psx_input_query_fifo_t)(void*);
+
+struct psx_input_t {
+    void* udata;
+
+    psx_input_write_t write_func;
+    psx_input_read_t read_func;
+    psx_input_on_button_press_t on_button_press_func;
+    psx_input_on_button_release_t on_button_release_func;
+    psx_input_on_analog_change_t on_analog_change_func;
+    psx_input_query_fifo_t query_fifo_func;
+};
+
+psx_input_t* psx_input_create();
+void psx_input_init(psx_input_t*);
+void psx_input_set_write_func(psx_input_t*, psx_input_write_t);
+void psx_input_set_read_func(psx_input_t*, psx_input_read_t);
+void psx_input_set_on_button_press_func(psx_input_t*, psx_input_on_button_press_t);
+void psx_input_set_on_button_release_func(psx_input_t*, psx_input_on_button_release_t);
+void psx_input_set_on_analog_change_func(psx_input_t*, psx_input_on_analog_change_t);
+void psx_input_set_query_fifo_func(psx_input_t*, psx_input_query_fifo_t);
+void psx_input_destroy(psx_input_t*);
+
+#endif
\ No newline at end of file
--- a/psx/dev/pad.c
+++ b/psx/dev/pad.c
@@ -5,6 +5,55 @@
 #include "pad.h"
 #include "../log.h"
 
+uint32_t pad_read_rx(psx_pad_t* pad) {
+    psx_input_t* current_slot = pad->slot[(pad->ctrl >> 13) & 1];
+
+    if (!current_slot)
+        return 0xffffffff;
+
+    if (!(pad->ctrl & CTRL_JOUT)) {
+        if (pad->ctrl & CTRL_RXEN) {
+            uint32_t data = current_slot->read_func(current_slot);
+
+            pad->ctrl &= ~CTRL_RXEN;
+
+            return data;
+        }
+
+        return 0xffffffff;
+    }
+
+    return current_slot->read_func(current_slot->udata);
+}
+
+void pad_write_tx(psx_pad_t* pad, uint16_t data) {
+    psx_input_t* current_slot = pad->slot[(pad->ctrl >> 13) & 1];
+
+    if (!current_slot)
+        return;
+
+    if (!(pad->ctrl & CTRL_TXEN))
+        return;
+    
+    pad->cycles_until_irq = 512;
+    
+    current_slot->write_func(current_slot->udata, data);
+}
+
+uint32_t pad_handle_stat_read(psx_pad_t* pad) {
+    return 0x07;
+    psx_input_t* current_slot = pad->slot[(pad->ctrl >> 13) & 1];
+
+    if (!current_slot)
+        return 0x5 | pad->stat;
+
+    return 0x5 | (current_slot->query_fifo_func(current_slot->udata) << 1);
+}
+
+void pad_handle_ctrl_write(psx_pad_t* pad, uint32_t value) {
+    pad->ctrl = value;
+}
+
 psx_pad_t* psx_pad_create() {
     return (psx_pad_t*)malloc(sizeof(psx_pad_t));
 }
@@ -16,23 +65,16 @@
 
     pad->io_base = PSX_PAD_BEGIN;
     pad->io_size = PSX_PAD_SIZE;
-
-    pad->rx_buf[0] = 0xff;
-    pad->rx_buf[1] = 0xff;
-    pad->rx_buf[2] = 0x41;
-    pad->rx_buf[3] = 0x5a;
-    pad->rx_buf[4] = 0xff;
-    pad->rx_buf[5] = 0xff;
 }
 
 uint32_t psx_pad_read32(psx_pad_t* pad, uint32_t offset) {
-    // switch (offset) {
-    //     case 0: return 0x00;
-    //     case 4: return 0x01;
-    //     case 8: return pad->mode;
-    //     case 10: return pad->ctrl;
-    //     case 14: return pad->baud;
-    // }
+    switch (offset) {
+        case 0: log_fatal("RX read 32"); return pad_read_rx(pad);
+        case 4: log_fatal("ST read 32"); return pad_handle_stat_read(pad);
+        case 8: log_fatal("MD read 32"); return pad->mode;
+        case 10: log_fatal("CT read 32"); return pad->ctrl;
+        case 14: log_fatal("BD read 32"); return pad->baud;
+    }
 
     log_fatal("Unhandled 32-bit PAD read at offset %08x", offset);
 
@@ -41,11 +83,11 @@
 
 uint16_t psx_pad_read16(psx_pad_t* pad, uint32_t offset) {
     switch (offset) {
-        // case 0: return 0x00;
-        case 4: return 0x07;
-        // case 8: return pad->mode;
-        case 10: return pad->ctrl;
-        // case 14: return pad->baud;
+        case 0: log_fatal("RX read 16"); return pad_read_rx(pad) & 0xffff;
+        case 4: log_fatal("ST read 16 %04x", pad_handle_stat_read(pad) & 0xffff); return pad_handle_stat_read(pad) & 0xffff;
+        case 8: log_fatal("MD read 16"); return pad->mode;
+        case 10: log_fatal("CT read 16 %04x", pad->ctrl & 0xffff); return pad->ctrl & 0xffff;
+        case 14: log_fatal("BD read 16"); return pad->baud;
     }
 
     log_fatal("Unhandled 16-bit PAD read at offset %08x", offset);
@@ -55,11 +97,11 @@
 
 uint8_t psx_pad_read8(psx_pad_t* pad, uint32_t offset) {
     switch (offset) {
-        case 0: return pad->rx_buf[(pad->rx_index++) % 6];
-        case 4: return 0x03;
-        case 8: return pad->mode;
-        case 10: return pad->ctrl;
-        case 14: return pad->baud;
+        case 0: log_fatal("RX read 8 %02x", pad_read_rx(pad) & 0xff); return pad_read_rx(pad) & 0xff;
+        case 4: log_fatal("ST read 8"); return pad_handle_stat_read(pad) & 0xff;
+        case 8: log_fatal("MD read 8"); return pad->mode & 0xff;
+        case 10: log_fatal("CT read 8"); return pad->ctrl & 0xff;
+        case 14: log_fatal("BD read 8"); return pad->baud & 0xff;
     }
 
     log_fatal("Unhandled 8-bit PAD read at offset %08x", offset);
@@ -68,13 +110,12 @@
 }
 
 void psx_pad_write32(psx_pad_t* pad, uint32_t offset, uint32_t value) {
-    // switch (offset) {
-    //     case 0: log_fatal("PAD write %08x", value); break;
-    //     case 4: break;
-    //     case 8: pad->mode = value & 0xffff; return;
-    //     case 10: pad->ctrl = value & 0xffff; return;
-    //     case 14: pad->baud = value & 0xffff; return;
-    // }
+    switch (offset) {
+        case 0: log_fatal("TX write 32 %08x", value); pad_write_tx(pad, value); return;
+        case 8: log_fatal("MD write 32 %08x", value); pad->mode = value & 0xffff; return;
+        case 10: log_fatal("CT write 32 %08x", value); pad_handle_ctrl_write(pad, value); return;
+        case 14: log_fatal("BD write 32 %08x", value); pad->baud = value & 0xffff; return;
+    }
 
     log_fatal("Unhandled 32-bit PAD write at offset %08x (%08x)", offset, value);
 }
@@ -81,11 +122,10 @@
 
 void psx_pad_write16(psx_pad_t* pad, uint32_t offset, uint16_t value) {
     switch (offset) {
-        // case 0: break;
-        // case 4: break;
-        // case 8: pad->mode = value; return;
-        case 10: pad->ctrl = value; return;
-        // case 14: pad->baud = value; return;
+        case 0: log_fatal("TX write 16 %04x", value); pad_write_tx(pad, value); return;
+        case 8: log_fatal("MD write 16 %04x", value); pad->mode = value; return;
+        case 10: log_fatal("CT write 16 %04x", value); pad_handle_ctrl_write(pad, value); return;
+        case 14: log_fatal("BD write 16 %04x", value); pad->baud = value; return;
     }
 
     log_fatal("Unhandled 16-bit PAD write at offset %08x (%04x)", offset, value);
@@ -93,22 +133,45 @@
 
 void psx_pad_write8(psx_pad_t* pad, uint32_t offset, uint8_t value) {
     switch (offset) {
-        case 0: return; break;
-        case 4: break;
-        case 8: pad->mode = value; return;
-        case 10: pad->ctrl = value; return;
-        case 14: pad->baud = value; return;
+        case 0: log_fatal("TX write 8 %02x", value); pad_write_tx(pad, value); return;
+        case 8: log_fatal("MD write 8 %02x", value); pad->mode = value; return;
+        case 10: log_fatal("CT write 8 %02x", value); pad_handle_ctrl_write(pad, value); return;
+        case 14: log_fatal("BD write 8 %02x", value); pad->baud = value; return;
     }
 
     log_fatal("Unhandled 8-bit PAD write at offset %08x (%02x)", offset, value);
 }
 
-void psx_pad_button_press(psx_pad_t* pad, uint16_t data) {
-    *((uint16_t*)(&pad->rx_buf[4])) &= ~data;
+void psx_pad_button_press(psx_pad_t* pad, int slot, uint16_t data) {
+    psx_input_t* selected_slot = pad->slot[slot];
+
+    if (selected_slot)
+        selected_slot->on_button_press_func(selected_slot->udata, data);
 }
 
-void psx_pad_button_release(psx_pad_t* pad, uint16_t data) {
-    *((uint16_t*)(&pad->rx_buf[4])) |= data;
+void psx_pad_button_release(psx_pad_t* pad, int slot, uint16_t data) {
+    psx_input_t* selected_slot = pad->slot[slot];
+
+    if (selected_slot)
+        selected_slot->on_button_release_func(selected_slot->udata, data);
+}
+
+void psx_pad_init_slot(psx_pad_t* pad, int slot, psx_input_t* input) {
+    pad->slot[slot] = input;
+}
+
+void psx_pad_update(psx_pad_t* pad, int cyc) {
+    if (pad->cycles_until_irq) {
+        pad->cycles_until_irq -= cyc;
+
+        if (pad->cycles_until_irq <= 0) {
+            psx_ic_irq(pad->ic, IC_JOY);
+
+            log_fatal("PAD IRQ");
+
+            pad->cycles_until_irq = 0;
+        }
+    }
 }
 
 void psx_pad_destroy(psx_pad_t* pad) {
--- a/psx/dev/pad.h
+++ b/psx/dev/pad.h
@@ -4,6 +4,7 @@
 #include <stdint.h>
 
 #include "ic.h"
+#include "input.h"
 
 #define PSX_PAD_BEGIN 0x1f801040
 #define PSX_PAD_SIZE  0x10
@@ -32,6 +33,62 @@
 #define PSXI_SW_SDA_SQUARE      0x8000
 
 /*
+  0     TX Ready Flag 1   (1=Ready/Started)
+  1     RX FIFO Not Empty (0=Empty, 1=Not Empty)
+  2     TX Ready Flag 2   (1=Ready/Finished)
+  3     RX Parity Error   (0=No, 1=Error; Wrong Parity, when enabled)  (sticky)
+  4     Unknown (zero)    (unlike SIO, this isn't RX FIFO Overrun flag)
+  5     Unknown (zero)    (for SIO this would be RX Bad Stop Bit)
+  6     Unknown (zero)    (for SIO this would be RX Input Level AFTER Stop bit)
+  7     /ACK Input Level  (0=High, 1=Low)
+  8     Unknown (zero)    (for SIO this would be CTS Input Level)
+  9     Interrupt Request (0=None, 1=IRQ7) (See JOY_CTRL.Bit4,10-12)   (sticky)
+  10    Unknown (always zero)
+  11-31 Baudrate Timer    (21bit timer, decrementing at 33MHz)
+*/
+
+#define STAT_TXR1 0x0001
+#define STAT_RXNE 0x0002
+#define STAT_TXR2 0x0004
+#define STAT_RXPE 0x0008
+#define STAT_UNK4 0x0010
+#define STAT_UNK5 0x0020
+#define STAT_UNK6 0x0040
+#define STAT_ACKL 0x0080
+#define STAT_UNK8 0x0100
+#define STAT_IRQ7 0x0200
+
+/*
+  0     TX Enable (TXEN)  (0=Disable, 1=Enable)
+  1     /JOYn Output      (0=High, 1=Low/Select) (/JOYn as defined in Bit13)
+  2     RX Enable (RXEN)  (0=Normal, when /JOYn=Low, 1=Force Enable Once)
+  3     Unknown? (read/write-able) (for SIO, this would be TX Output Level)
+  4     Acknowledge       (0=No change, 1=Reset JOY_STAT.Bits 3,9)          (W)
+  5     Unknown? (read/write-able) (for SIO, this would be RTS Output Level)
+  6     Reset             (0=No change, 1=Reset most JOY_registers to zero) (W)
+  7     Not used             (always zero) (unlike SIO, no matter of FACTOR)
+  8-9   RX Interrupt Mode    (0..3 = IRQ when RX FIFO contains 1,2,4,8 bytes)
+  10    TX Interrupt Enable  (0=Disable, 1=Enable) ;when JOY_STAT.0-or-2 ;Ready
+  11    RX Interrupt Enable  (0=Disable, 1=Enable) ;when N bytes in RX FIFO
+  12    ACK Interrupt Enable (0=Disable, 1=Enable) ;when JOY_STAT.7  ;/ACK=LOW
+  13    Desired Slot Number  (0=/JOY1, 1=/JOY2) (set to LOW when Bit1=1)
+  14-15 Not used             (always zero)
+*/
+
+#define CTRL_TXEN 0x0001
+#define CTRL_JOUT 0x0002
+#define CTRL_RXEN 0x0004
+#define CTRL_UNK3 0x0008
+#define CTRL_ACKN 0x0010
+#define CTRL_UNK5 0x0020
+#define CTRL_NUS7 0x0040
+#define CTRL_RXIM 0x0180
+#define CTRL_TXIE 0x0200
+#define CTRL_RXIE 0x0400
+#define CTRL_ACIE 0x0800
+#define CTRL_SLOT 0x1000
+
+/*
     To-do: Design API to interface any type of controller.
 
     Possible names:
@@ -53,11 +110,13 @@
     uint32_t io_base, io_size;
 
     psx_ic_t* ic;
+    psx_input_t* slot[2];
 
-    uint8_t rx_buf[6];
-    int rx_index;
+    int enable_once;
+    int cycles_until_irq;
+    int cycle_counter;
 
-    uint16_t mode, ctrl, baud;
+    uint16_t mode, ctrl, baud, stat;
 } psx_pad_t;
 
 psx_pad_t* psx_pad_create();
@@ -69,7 +128,9 @@
 void psx_pad_write16(psx_pad_t*, uint32_t, uint16_t);
 void psx_pad_write8(psx_pad_t*, uint32_t, uint8_t);
 void psx_pad_destroy(psx_pad_t*);
-void psx_pad_button_press(psx_pad_t*, uint16_t);
-void psx_pad_button_release(psx_pad_t*, uint16_t);
+void psx_pad_button_press(psx_pad_t*, int, uint16_t);
+void psx_pad_button_release(psx_pad_t*, int, uint16_t);
+void psx_pad_init_slot(psx_pad_t*, int, psx_input_t*);
+void psx_pad_update(psx_pad_t*, int);
 
 #endif
\ No newline at end of file
--- /dev/null
+++ b/psx/input/sda.c
@@ -1,0 +1,115 @@
+/*
+    This file is part of the PSXE Emulator Project
+
+    Sony PlayStation Standard Digital/Analog Controller Emulator
+*/
+
+#include "sda.h"
+#include "../log.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+psxi_sda_t* psxi_sda_create() {
+    return (psxi_sda_t*)malloc(sizeof(psxi_sda_t));
+}
+
+void psxi_sda_init(psxi_sda_t* sda, uint16_t model) {
+    memset(sda, 0, sizeof(psxi_sda_t));
+
+    sda->tx_data = 0xff;
+    sda->model = model;
+    sda->state = SDA_STATE_WFC;
+    sda->sw = 0xffff;
+}
+
+uint32_t psxi_sda_read(void* udata) {
+    psxi_sda_t* sda = (psxi_sda_t*)udata;
+
+    return sda->tx_data;
+}
+
+void psxi_sda_write(void* udata, uint16_t data) {
+    psxi_sda_t* sda = (psxi_sda_t*)udata;
+
+    switch (sda->state) {
+        case SDA_STATE_WFC: {
+            if (data == 0x01) {
+                sda->tx_data_ready = 1;
+                sda->tx_data = 0xff;
+                sda->state = SDA_STATE_WFR;
+            }
+        } break;
+
+        case SDA_STATE_WFR: {
+            if (data == 'B') {
+                sda->tx_data = sda->model;
+                sda->state = SDA_STATE_TX_IDH;
+            }
+        } break;
+
+        case SDA_STATE_TX_IDH: {
+            // To-do: Handle MOT
+            sda->tx_data = 0x5a;
+            sda->state = SDA_STATE_TX_SWL;
+        } break;
+
+        case SDA_STATE_TX_SWL: {
+            sda->tx_data = sda->sw & 0xff;
+            sda->state = SDA_STATE_TX_SWH;
+        } break;
+
+        case SDA_STATE_TX_SWH: {
+            sda->tx_data = sda->sw >> 8;
+
+            switch (sda->model) {
+                case 0x41:
+                    sda->state = SDA_STATE_WFC;
+                break;
+
+                // To-do: Implement analog mode
+                case 0x73:
+                case 0x53:
+                    sda->state = SDA_STATE_WFC;
+                break;
+            }
+        } break;
+    }
+}
+
+void psxi_sda_on_button_press(void* udata, uint16_t data) {
+    psxi_sda_t* sda = (psxi_sda_t*)udata;
+
+    sda->sw &= ~data;
+}
+
+void psxi_sda_on_button_release(void* udata, uint16_t data) {
+    psxi_sda_t* sda = (psxi_sda_t*)udata;
+
+    sda->sw |= data;
+}
+
+// To-do: Implement analog mode
+void psxi_sda_on_analog_change(void* udata, uint16_t data) {
+    psxi_sda_t* sda = (psxi_sda_t*)udata;
+}
+
+int psxi_sda_query_fifo(void* udata) {
+    psxi_sda_t* sda = (psxi_sda_t*)udata;
+
+    return sda->tx_data_ready;
+}
+
+void psxi_sda_init_input(psxi_sda_t* sda, psx_input_t* input) {
+    input->udata = sda;
+    input->write_func = psxi_sda_write;
+    input->read_func = psxi_sda_read;
+    input->on_button_press_func = psxi_sda_on_button_press;
+    input->on_button_release_func = psxi_sda_on_button_release;
+    input->on_analog_change_func = psxi_sda_on_analog_change;
+    input->query_fifo_func = psxi_sda_query_fifo;
+}
+
+void psxi_sda_destroy(psxi_sda_t* sda) {
+    free(sda);
+}
\ No newline at end of file
--- /dev/null
+++ b/psx/input/sda.h
@@ -1,0 +1,61 @@
+/*
+    This file is part of the PSXE Emulator Project
+
+    Sony PlayStation Standard Digital/Analog Controller Emulator
+*/
+
+#ifndef SDA_H
+#define SDA_H
+
+#include "../dev/input.h"
+
+// Controller/Input IDs
+#define SDA_MODEL_DIGITAL       0x41
+#define SDA_MODEL_ANALOG_PAD    0x73
+#define SDA_MODEL_ANALOG_STICK  0x53
+
+#define PSXI_SW_SDA_SELECT      0x0001
+#define PSXI_SW_SDA_L3          0x0002
+#define PSXI_SW_SDA_R3          0x0004
+#define PSXI_SW_SDA_START       0x0008
+#define PSXI_SW_SDA_PAD_UP      0x0010
+#define PSXI_SW_SDA_PAD_RIGHT   0x0020
+#define PSXI_SW_SDA_PAD_DOWN    0x0040
+#define PSXI_SW_SDA_PAD_LEFT    0x0080
+#define PSXI_SW_SDA_L2          0x0100
+#define PSXI_SW_SDA_R2          0x0200
+#define PSXI_SW_SDA_L1          0x0400
+#define PSXI_SW_SDA_R1          0x0800
+#define PSXI_SW_SDA_TRIANGLE    0x1000
+#define PSXI_SW_SDA_CIRCLE      0x2000
+#define PSXI_SW_SDA_CROSS       0x4000
+#define PSXI_SW_SDA_SQUARE      0x8000
+
+enum {
+    SDA_STATE_WFC = 0,
+    SDA_STATE_WFR,
+    SDA_STATE_TX_IDH,
+    SDA_STATE_TX_SWL,
+    SDA_STATE_TX_SWH
+};
+
+enum {
+    SA_MODE_DIGITAL = 0,
+    SA_MODE_ANALOG
+};
+
+typedef struct {
+    uint8_t model;
+    int state;
+    int sa_mode;
+    uint16_t sw;
+    uint8_t tx_data;
+    int tx_data_ready;
+} psxi_sda_t;
+
+psxi_sda_t* psxi_sda_create();
+void psxi_sda_init(psxi_sda_t*, uint16_t);
+void psxi_sda_init_input(psxi_sda_t*, psx_input_t*);
+void psxi_sda_destroy(psxi_sda_t*);
+
+#endif
\ No newline at end of file
--- a/psx/psx.c
+++ b/psx/psx.c
@@ -28,6 +28,7 @@
     psx_cpu_cycle(psx->cpu);
     psx_cdrom_update(psx->cdrom);
     psx_gpu_update(psx->gpu, psx->cpu->last_cycles);
+    psx_pad_update(psx->pad, psx->cpu->last_cycles);
 }
 
 void psx_run_frame(psx_t* psx) {
--