From daa0c1c13d80f8980be9d24f1b87983cac24624c Mon Sep 17 00:00:00 2001 From: Jacques Gagnon Date: Thu, 23 Jul 2020 20:31:16 -0400 Subject: [PATCH] [WIRED] Add NES/SNES PISO driver --- main/CMakeLists.txt | 1 + main/main.c | 5 +- main/wired/npiso.c | 353 ++++++++++++++++++++++++++++++++++++++++++++ main/wired/npiso.h | 6 + 4 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 main/wired/npiso.c create mode 100644 main/wired/npiso.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 84ed32d5..6cc626ca 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -27,6 +27,7 @@ idf_component_register(SRCS "main.c" "bluetooth/hidp_sw.c" "drivers/sd.c" "wired/detect.c" + "wired/npiso.c" "wired/sega_io.c" "wired/nsi.c" "wired/maple.c" diff --git a/main/main.c b/main/main.c index f69e6487..34d092a2 100644 --- a/main/main.c +++ b/main/main.c @@ -6,6 +6,7 @@ #include "adapter/config.h" #include "bluetooth/host.h" #include "wired/detect.h" +#include "wired/npiso.h" #include "wired/sega_io.h" #include "wired/nsi.h" #include "wired/maple.h" @@ -30,11 +31,11 @@ static const char *sys_name[WIRED_MAX] = { static const wired_init_t wired_init[WIRED_MAX] = { NULL, - NULL, + npiso_init, NULL, NULL, sega_io_init, - NULL, + npiso_init, NULL, sega_io_init, nsi_init, diff --git a/main/wired/npiso.c b/main/wired/npiso.c new file mode 100644 index 00000000..c1a1b832 --- /dev/null +++ b/main/wired/npiso.c @@ -0,0 +1,353 @@ +#include +#include +#include +#include +#include +#include +#include +#include "driver/gpio.h" +#include "../zephyr/types.h" +#include "../util.h" +#include "../adapter/adapter.h" +#include "../adapter/config.h" +#include "npiso.h" + +#define NPISO_PORT_MAX 2 +#define NPISO_LATCH_PIN 32 +#define NPISO_LATCH_MASK (1U << 0) /* First of 2nd bank of GPIO */ +#define NPISO_TIMEOUT 4096 + +enum { + NPISO_CLK = 0, + NPISO_SEL, + NPISO_D0, + NPISO_D1, + NPISO_PIN_MAX, +}; + +enum { + DEV_NONE = 0, + DEV_FC_NES_PAD, + DEV_FC_NES_MULTITAP, + DEV_FC_MULTITAP_ALT, + DEV_FC_TRACKBALL, + DEV_FC_KB, + DEV_SFC_SNES_PAD, + DEV_SFC_SNES_MULTITAP, + DEV_SFC_SNES_MOUSE, + DEV_SNES_XBAND_KB, +}; + +static const uint8_t gpio_pins[NPISO_PORT_MAX][NPISO_PIN_MAX] = { + { 5, 23, 19, 21}, + {18, 26, 22, 25}, +}; + +static const uint32_t gpio_mask[NPISO_PORT_MAX][NPISO_PIN_MAX] = { + {(1U << 5), (1U << 23), (1U << 19), (1U << 21)}, + {(1U << 18), (1U << 26), (1U << 22), (1U << 25)}, +}; + +static uint8_t dev_type[NPISO_PORT_MAX] = {0}; +static uint8_t mt_first_port[NPISO_PORT_MAX] = {0}; +static uint8_t buffer[8]; +static uint32_t cnt[2] = {0}; +static uint8_t mask[2] = {0x80, 0x80}; + +static void IRAM_ATTR set_data(uint8_t port, uint8_t data_id, uint8_t value) { + uint8_t pin = gpio_pins[port][NPISO_D0 + data_id]; + + if (value) { + GPIO.out_w1ts = BIT(pin); + } + else { + GPIO.out_w1tc = BIT(pin); + } +} + +static void IRAM_ATTR npiso_fc_nes_2p_isr(void* arg) { + const uint32_t low_io = GPIO.acpu_int; + const uint32_t high_io = GPIO.acpu_int1.intr; + + /* reset bit counter, set first bit */ + if (high_io & NPISO_LATCH_MASK) { + set_data(0, 0, wired_adapter.data[0].output[0] & 0x80); + set_data(1, 0, wired_adapter.data[1].output[0] & 0x80); + cnt[0] = 1; + cnt[1] = 1; + mask[0] = 0x40; + mask[1] = 0x40; + } + + /* Update port 0 */ + if (low_io & gpio_mask[0][NPISO_CLK]) { + if (cnt[0] > 7) { + set_data(0, 0, 0); + } + else { + set_data(0, 0, wired_adapter.data[0].output[0] & mask[0]); + } + cnt[0]++; + mask[0] >>= 1; + } + + /* Update port 1 */ + if (low_io & gpio_mask[1][NPISO_CLK]) { + if (cnt[1] > 7) { + set_data(1, 0, 0); + } + else { + set_data(1, 0, wired_adapter.data[1].output[0] & mask[1]); + } + cnt[1]++; + mask[1] >>= 1; + } + + if (high_io) GPIO.status1_w1tc.intr_st = high_io; + if (low_io) GPIO.status_w1tc = low_io; +} + +static void IRAM_ATTR npiso_fc_4p_isr(void* arg) { + const uint32_t low_io = GPIO.acpu_int; + const uint32_t high_io = GPIO.acpu_int1.intr; + + /* reset bit counter, set first bit */ + if (high_io & NPISO_LATCH_MASK) { + set_data(0, 0, wired_adapter.data[0].output[0] & 0x80); + set_data(1, 0, wired_adapter.data[1].output[0] & 0x80); + set_data(0, 1, wired_adapter.data[2].output[0] & 0x80); + set_data(1, 1, wired_adapter.data[3].output[0] & 0x80); + cnt[0] = 1; + cnt[1] = 1; + mask[0] = 0x40; + mask[1] = 0x40; + } + + /* Update port 0 */ + if (low_io & gpio_mask[0][NPISO_CLK]) { + if (cnt[0] > 7) { + set_data(0, 0, 0); + set_data(0, 1, 0); + } + else { + set_data(0, 0, wired_adapter.data[0].output[0] & mask[0]); + set_data(0, 1, wired_adapter.data[2].output[0] & mask[0]); + } + cnt[0]++; + mask[0] >>= 1; + } + + /* Update port 1 */ + if (low_io & gpio_mask[1][NPISO_CLK]) { + if (cnt[1] > 7) { + set_data(1, 0, 0); + set_data(1, 1, 0); + } + else { + set_data(1, 0, wired_adapter.data[1].output[0] & mask[1]); + set_data(1, 1, wired_adapter.data[3].output[0] & mask[1]); + } + cnt[1]++; + mask[1] >>= 1; + } + + if (high_io) GPIO.status1_w1tc.intr_st = high_io; + if (low_io) GPIO.status_w1tc = low_io; +} + +static void IRAM_ATTR npiso_nes_fs_isr(void* arg) { + const uint32_t low_io = GPIO.acpu_int; + const uint32_t high_io = GPIO.acpu_int1.intr; + uint32_t idx0, idx1; + + /* reset bit counter, set first bit */ + if (high_io & NPISO_LATCH_MASK) { + set_data(0, 0, wired_adapter.data[0].output[0] & 0x80); + set_data(1, 0, wired_adapter.data[1].output[0] & 0x80); + cnt[0] = 1; + cnt[1] = 1; + mask[0] = 0x40; + mask[1] = 0x40; + } + + idx0 = cnt[0] >> 3; + idx1 = cnt[1] >> 3; + + /* Update port 0 */ + if (low_io & gpio_mask[0][NPISO_CLK]) { + switch (idx0) { + case 0: + set_data(0, 0, wired_adapter.data[0].output[0] & mask[0]); + break; + case 1: + set_data(0, 0, wired_adapter.data[2].output[0] & mask[0]); + break; + case 2: + set_data(0, 0, 0xEF & mask[0]); + break; + default: + set_data(0, 0, 0); + break; + } + cnt[0]++; + mask[0] >>= 1; + if (!mask[0]) { + mask[0] = 0x80; + } + } + + /* Update port 1 */ + if (low_io & gpio_mask[1][NPISO_CLK]) { + switch (idx1) { + case 0: + set_data(1, 0, wired_adapter.data[1].output[0] & mask[1]); + break; + case 1: + set_data(1, 0, wired_adapter.data[3].output[0] & mask[1]); + break; + case 2: + set_data(1, 0, 0xDF & mask[1]); + break; + default: + set_data(1, 0, 0); + break; + } + cnt[1]++; + mask[1] >>= 1; + if (!mask[1]) { + mask[1] = 0x80; + } + } + + if (high_io) GPIO.status1_w1tc.intr_st = high_io; + if (low_io) GPIO.status_w1tc = low_io; +} + +void npiso_init(void) +{ + gpio_config_t io_conf = {0}; + + if (wired_adapter.system_id == NES) { + switch (config.global_cfg.multitap_cfg) { + case MT_SLOT_1: + case MT_SLOT_2: + case MT_DUAL: + dev_type[0] = DEV_FC_NES_MULTITAP; + dev_type[1] = DEV_FC_NES_MULTITAP; + mt_first_port[1] = 2; + break; + case MT_ALT: + dev_type[0] = DEV_FC_MULTITAP_ALT; + dev_type[1] = DEV_FC_MULTITAP_ALT; + break; + default: + mt_first_port[1] = 1; + break; + } + + for (uint32_t i = 0; i < NPISO_PORT_MAX; i++) { + if (dev_type[i] == DEV_NONE) { + switch (config.out_cfg[i].dev_mode) { + case DEV_PAD: + case DEV_PAD_ALT: + dev_type[i] = DEV_FC_NES_PAD; + break; + case DEV_KB: + dev_type[i] = DEV_FC_KB; + break; + case DEV_MOUSE: + dev_type[i] = DEV_FC_TRACKBALL; + break; + } + } + } + } + else { + switch (config.global_cfg.multitap_cfg) { + case MT_SLOT_1: + dev_type[0] = DEV_SFC_SNES_MULTITAP; + mt_first_port[1] = 4; + break; + case MT_SLOT_2: + dev_type[1] = DEV_SFC_SNES_MULTITAP; + mt_first_port[1] = 1; + break; + case MT_DUAL: + dev_type[0] = DEV_SFC_SNES_MULTITAP; + dev_type[1] = DEV_SFC_SNES_MULTITAP; + mt_first_port[1] = 4; + break; + default: + mt_first_port[1] = 1; + break; + } + + for (uint32_t i = 0; i < NPISO_PORT_MAX; i++) { + if (dev_type[i] == DEV_NONE) { + switch (config.out_cfg[i].dev_mode) { + case DEV_PAD: + case DEV_PAD_ALT: + dev_type[i] = DEV_SFC_SNES_PAD; + break; + case DEV_KB: + dev_type[i] = DEV_SNES_XBAND_KB; + break; + case DEV_MOUSE: + dev_type[i] = DEV_SFC_SNES_MOUSE; + break; + } + } + } + } + + /* Latch */ + io_conf.intr_type = GPIO_PIN_INTR_POSEDGE; + io_conf.pin_bit_mask = 1ULL << NPISO_LATCH_PIN; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&io_conf); + + /* Clocks */ + for (uint32_t i = 0; i < NPISO_PORT_MAX; i++) { + io_conf.intr_type = GPIO_PIN_INTR_POSEDGE; + io_conf.pin_bit_mask = 1ULL << gpio_pins[i][NPISO_CLK]; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&io_conf); + } + + /* Selects */ + for (uint32_t i = 0; i < NPISO_PORT_MAX; i++) { + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.pin_bit_mask = 1ULL << gpio_pins[i][NPISO_SEL]; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&io_conf); + } + + /* D0, D1 */ + for (uint32_t i = 0; i < NPISO_PORT_MAX; i++) { + for (uint32_t j = NPISO_D0; j <= NPISO_D1; j++) { + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.pin_bit_mask = 1ULL << gpio_pins[i][j]; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&io_conf); + set_data(i, j & 0x1, 1); + } + } + + if (dev_type[0] == DEV_FC_NES_MULTITAP) { + esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL3, npiso_nes_fs_isr, NULL, NULL); + } + else if (dev_type[0] == DEV_FC_MULTITAP_ALT) { + esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL3, npiso_fc_4p_isr, NULL, NULL); + } + else { + esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL3, npiso_fc_nes_2p_isr, NULL, NULL); + } +} diff --git a/main/wired/npiso.h b/main/wired/npiso.h new file mode 100644 index 00000000..3461ad48 --- /dev/null +++ b/main/wired/npiso.h @@ -0,0 +1,6 @@ +#ifndef _NPISO_H_ +#define _NPISO_H_ + +void npiso_init(void); + +#endif /* _NPISO_H_ */