quack/Misc/esp_8_bit/controller.diff
2024-08-29 11:11:14 +02:00

880 lines
22 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

diff --git a/src/ir_input.h b/src/ir_input.h
deleted file mode 100644
index f02bde8..0000000
--- a/src/ir_input.h
+++ /dev/null
@@ -1,672 +0,0 @@
-
-/* Copyright (c) 2010-2020, Peter Barrett
- **
- ** Permission to use, copy, modify, and/or distribute this software for
- ** any purpose with or without fee is hereby granted, provided that the
- ** above copyright notice and this permission notice appear in all copies.
- **
- ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
- ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
- ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- ** SOFTWARE.
- */
-
-#ifndef __ir_input__
-#define __ir_input__
-
-// generate events on state changes of IR pin, feed to state machines.
-// event timings have a resolution of HSYNC, timing is close enough between 15720 and 15600 to make this work
-// poll to synthesize hid events at every frame
-// i know there is a perfectly good peripheral for this in the ESP32 but this seems more fun somehow
-
-#define WEBTV_KEYBOARD
-#define RETCON_CONTROLLER
-#define FLASHBACK_CONTROLLER
-//#define APPLE_TV_CONTROLLER
-
-uint8_t _ir_last = 0;
-uint8_t _ir_count = 0;
-uint8_t _keyDown = 0;
-uint8_t _keyUp = 0;
-
-void IRAM_ATTR ir_event(uint8_t ticks, uint8_t value); // t is HSYNCH ticks, v is value
-
-inline void IRAM_ATTR ir_sample()
-{
- uint8_t ir = (GPIO.in & (1 << IR_PIN)) != 0;
- if (ir != _ir_last)
- {
- ir_event(_ir_count,_ir_last);
- _ir_count = 0;
- _ir_last = ir;
- }
- if (_ir_count != 0xFF)
- _ir_count++;
-}
-
-class IRState {
-public:
- uint8_t _state = 0;
- uint16_t _code = 0;
- uint16_t _output = 0;
- uint16_t _joy[2] = {0};
- uint16_t _joy_last[2] = {0};
- uint8_t _timer[2] = {0};
-
- void set(int i, int m, int t)
- {
- uint8_t b = 0;
- if ((m & (GENERIC_LEFT | GENERIC_RIGHT)) == (GENERIC_LEFT | GENERIC_RIGHT))
- b++; // bogus?
- if ((m & (GENERIC_UP | GENERIC_DOWN)) == (GENERIC_UP | GENERIC_DOWN))
- b++; // bogus?
- if (b) {
- printf("bogus:%04X\n",m);
- return;
- }
-
- _joy[i] = m;
- _timer[i] = t;
- }
-
- // make a fake hid event
- int get_hid(uint8_t* dst)
- {
- if (_timer[0] && !--_timer[0])
- _joy[0] = 0;
- if (_timer[1] && !--_timer[1])
- _joy[1] = 0;
- if ((_joy[0] != _joy_last[0]) || (_joy[1] != _joy_last[1])) {
- _joy_last[0] = _joy[0];
- _joy_last[1] = _joy[1];
- dst[0] = 0xA1;
- dst[1] = 0x42;
- dst[2] = _joy[0];
- dst[3] = _joy[0]>>8;
- dst[4] = _joy[1];
- dst[5] = _joy[1]>>8;
- return 6; // only sent if it changes
- }
- return 0;
- }
-};
-
-//==========================================================
-//==========================================================
-// Apple remote NEC code
-// pretty easy to adapt to any NEC remote
-
-#ifdef APPLE_TV_CONTROLLER
-
-// Silver apple remote, 7 Bit code
-// should work with both silvers and white
-#define APPLE_MENU 0x40
-#define APPLE_PLAY 0x7A
-#define APPLE_CENTER 0x3A
-#define APPLE_RIGHT 0x60
-#define APPLE_LEFT 0x10
-#define APPLE_UP 0x50
-#define APPLE_DOWN 0x30
-
-#define APPLE_RELEASE 0x20 // sent after menu and play?
-
-// generic repeat code
-#define NEC_REPEAT 0xAAAA
-
-/*
- 9ms preamble ~142
- 4.5ms 1 ~71 - start
- 2.25ms ~35 - repeat
-
- 32 bits
- 0 a 562.5µs/562.5µs 9ish wide
- 1 a 562.5µs/1.6875ms 27ish wide
-*/
-
-IRState _apple;
-int get_hid_apple(uint8_t* dst)
-{
- if (_apple._output)
- {
- if (_apple._output != NEC_REPEAT)
- _keyDown = (_apple._output >> 8) & 0x7F; // 7 bit code
- _apple._output = 0;
-
- uint16_t k = 0;
- switch (_keyDown) {
- case APPLE_UP: k = GENERIC_UP; break;
- case APPLE_DOWN: k = GENERIC_DOWN; break;
- case APPLE_LEFT: k = GENERIC_LEFT; break;
- case APPLE_RIGHT: k = GENERIC_RIGHT; break;
- case APPLE_CENTER: k = GENERIC_FIRE; break;
- case APPLE_MENU: k = GENERIC_RESET; break;
- case APPLE_PLAY: k = GENERIC_SELECT; break;
- }
- _apple.set(0,k,15); // 108ms repeat period
- }
- return _apple.get_hid(dst);
-}
-
-// NEC codes used by apple remote
-void IRAM_ATTR ir_apple(uint8_t t, uint8_t v)
-{
- if (!v) {
- if (t > 32)
- _apple._state = 0;
- } else {
- if (t < 32)
- {
- _apple._code <<= 1;
- if (t >= 12)
- _apple._code |= 1;
- if (++_apple._state == 32)
- _apple._output = _apple._code; // Apple code in bits 14-8*
- } else {
- if (t > 32 && t < 40 && !_apple._state) // Repeat 2.25ms pulse 4.5ms start
- _apple._output = NEC_REPEAT;
- _apple._state = 0;
- }
- }
-}
-
-#endif
-
-//==========================================================
-//==========================================================
-// Atari Flashback 4 wireless controllers
-
-#ifdef FLASHBACK_CONTROLLER
-
-// HSYNCH period is 44/315*455 or 63.55555..us
-// 18 bit code 1.87khz clock
-// 2.3ms zero preamble // 36
-// 0 is 0.27ms pulse // 4
-// 1 is 0.80ms pulse // 13
-
-// Keycodes...
-// Leading bit is 1 for player 1 control..
-
-#define PREAMBLE(_t) (_t >= 34 && _t <= 38)
-#define SHORT(_t) (_t >= 2 && _t <= 6)
-#define LONG(_t) (_t >= 11 && _t <= 15)
-
-// Codes come in pairs 33ms apart
-// Sequence repeats every 133ms
-// bitmap is released if no code for 10 vbls (167ms) or 0x01 (p1) / 0x0F (p2)
-// up to 12 button bits, 4 bits of csum/p1/p2 indication
-
-// called at every loop ~60Hz
-
-IRState _flashback;
-int get_hid_flashback(uint8_t* dst)
-{
- if (_flashback._output)
- {
- uint16_t m = _flashback._output >> 4; // 12 bits of buttons
- printf("F:%04X\n",m);
- uint8_t csum = _flashback._output & 0xF; // csum+1 for p1, csum-1 for p2
- uint8_t s = m + (m >> 4) + (m >> 8);
- if (((s+1) & 0xF) == csum)
- _flashback.set(0,m,15);
- else if (((s-1) & 0xF) == csum)
- _flashback.set(1,m,20);
- _flashback._output = 0;
- }
- return _flashback.get_hid(dst);
-}
-
-void IRAM_ATTR ir_flashback(uint8_t t, uint8_t v)
-{
- if (_flashback._state == 0)
- {
- if (PREAMBLE(t) && (v == 0)) // long 0, rising edge of start bit
- {
- _flashback._state = 1;
- }
- }
- else
- {
- if (v)
- {
- _flashback._code <<= 1;
- if (LONG(t))
- {
- _flashback._code |= 1;
- }
- else if (!SHORT(t))
- {
- _flashback._state = 0; // framing error
- return;
- }
-
- if (++_flashback._state == 19)
- {
- _flashback._output = _flashback._code;
- _flashback._state = 0;
- }
- }
- else
- {
- if (!SHORT(t))
- _flashback._state = 0; // Framing err
- }
- }
-}
-
-#endif
-
-//==========================================================
-//==========================================================
-// RETCON controllers
-// 75ms keyboard repeat
-// Preamble is 0.80ms low, 0.5 high
-// Low: 0.57ms=0,0.27,s=1, high 0.37
-// 16 bits
-// Preamble = 800,500/63.55555 ~ 12.6,7.87
-// LOW0 = 8.97
-// LOW1 = 4.25
-// HIGH = 5.82
-
-#ifdef RETCON_CONTROLLER
-
-// number of 63.55555 cycles per bit
-#define PREAMBLE_L(_t) (_t >= 12 && _t <= 14) // 12/13/14 preamble
-#define PREAMBLE_H(_t) (_t >= 6 && _t <= 10) // 8
-#define LOW_0(_t) (_t >= 8 && _t <= 10) // 8/9/10
-#define LOW_1(_t) (_t >= 4 && _t <= 6) // 4/5/6
-
-// map retcon to generic
-const uint16_t _jmap[] = {
- 0x0400, GENERIC_UP,
- 0x0200, GENERIC_DOWN,
- 0x0100, GENERIC_LEFT,
- 0x0080, GENERIC_RIGHT,
-
- 0x1000, GENERIC_SELECT,
- 0x0800, GENERIC_START,
-
- 0x0020, GENERIC_FIRE_X,
- 0x0040, GENERIC_FIRE_Y,
- 0x0002, GENERIC_FIRE_Z,
-
- 0x2000, GENERIC_FIRE_A,
- 0x4000, GENERIC_FIRE_B,
- 0x0008, GENERIC_FIRE_C,
-};
-
-IRState _retcon;
-int get_hid_retcon(uint8_t* dst)
-{
- if (_retcon._output)
- {
- uint16_t m = 0;
- const uint16_t* jmap = _jmap;
- int16_t i = 12;
- uint16_t k = _retcon._output;
- _retcon._output = 0;
- while (i--)
- {
- if (jmap[0] & k)
- m |= jmap[1];
- jmap += 2;
- }
- printf("R:%04X\n",m);
- _retcon.set(k >> 15,m,20);
- }
- return _retcon.get_hid(dst);
-}
-
-void IRAM_ATTR ir_retcon(uint8_t t, uint8_t v)
-{
- if (_retcon._state == 0)
- {
- if (v == 0) { // start bit
- if (PREAMBLE_L(t))
- _retcon._state = 1;
- }
- }
- else
- {
- if (!v)
- {
- _retcon._code <<= 1;
- if (LOW_1(t))
- _retcon._code |= 1;
- if (_retcon._state++ == 16)
- {
- _retcon._output = _retcon._code;
- _retcon._state = 0;
- }
- }
- }
-}
-
-#endif
-
-
-//==========================================================
-//==========================================================
-// Webtv keyboard
-#ifdef WEBTV_KEYBOARD
-
-#define BAUDB 12 // Width of uart bit in HSYNCH
-#define WT_PREAMBLE(_t) (_t >= 36 && _t <= 40) // 3.25 baud
-#define SHORTBIT(_t) (_t >= 9 && _t <= 13) // 1.5ms ish
-
-// converts webtv keyboard to common scancodes
-const uint8_t _ir2scancode[128] = {
- 0x00, // 00
- 0x00, // 02
- 0x05, // 04 B
- 0x00, // 06
- 0x00, // 08
- 0x51, // 0A Down
- 0x00, // 0C
- 0x00, // 0E
- 0x00, // 10
- 0x50, // 12 Left
- 0xE6, // 14 Right Alt
- 0x38, // 16 /
- 0xE2, // 18 Left Alt
- 0x4F, // 1A Right
- 0x2C, // 1C Space
- 0x11, // 1E N
- 0x32, // 20 #
- 0x00, // 22
- 0x22, // 24 5
- 0x41, // 26 F8
- 0x3B, // 28 F2
- 0xE4, // 2A Right Ctrl
- 0x00, // 2C
- 0x2E, // 2E =
- 0x3A, // 30 F1
- 0x4A, // 32 Home
- 0x00, // 34
- 0x2D, // 36 -
- 0xE0, // 38 Left Ctrl
- 0x35, // 3A `
- 0x42, // 3C F9
- 0x23, // 3E 6
- 0x00, // 40
- 0x00, // 42
- 0x19, // 44 V
- 0x37, // 46 .
- 0x06, // 48 C
- 0x68, // 4A F13
- 0xE5, // 4C Right Shift
- 0x36, // 4E ,
- 0x1B, // 50 X
- 0x4D, // 52 End
- 0x00, // 54
- 0x00, // 56
- 0x1D, // 58 Z
- 0x00, // 5A
- 0x28, // 5C Return
- 0x10, // 5E M
- 0x00, // 60
- 0xE7, // 62 Right GUI
- 0x09, // 64 F
- 0x0F, // 66 L
- 0x07, // 68 D
- 0x4E, // 6A PageDown
- 0x00, // 6C
- 0x0E, // 6E K
- 0x16, // 70 S
- 0x4B, // 72 PageUp
- 0x00, // 74
- 0x33, // 76 ;
- 0x04, // 78 A
- 0x00, // 7A
- 0x31, // 7C |
- 0x0D, // 7E J
- 0x00, // 80
- 0x00, // 82
- 0x17, // 84 T
- 0x40, // 86 F7
- 0x3C, // 88 F3
- 0x00, // 8A
- 0xE1, // 8C Left Shift
- 0x30, // 8E ]
- 0x39, // 90 CapsLock
- 0x00, // 92
- 0x29, // 94 Escape
- 0x2F, // 96 [
- 0x2B, // 98 Tab
- 0x00, // 9A
- 0x2A, // 9C Backspace
- 0x1C, // 9E Y
- 0x00, // A0
- 0x00, // A2
- 0x21, // A4 4
- 0x26, // A6 9
- 0x20, // A8 3
- 0x44, // AA F11
- 0x00, // AC
- 0x25, // AE 8
- 0x1F, // B0 2
- 0x00, // B2
- 0x46, // B4 PrintScreen
- 0x27, // B6 0
- 0x1E, // B8 1
- 0x45, // BA F12
- 0x43, // BC F10
- 0x24, // BE 7
- 0x00, // C0
- 0x00, // C2
- 0x0A, // C4 G
- 0x00, // C6
- 0x3D, // C8 F4
- 0x00, // CA
- 0x00, // CC
- 0x00, // CE
- 0x3E, // D0 F5
- 0x52, // D2 Up
- 0xE3, // D4 Left GUI
- 0x34, // D6 '
- 0x29, // D8 Escape
- 0x48, // DA Pause
- 0x3F, // DC F6
- 0x0B, // DE H
- 0x00, // E0
- 0x00, // E2
- 0x15, // E4 R
- 0x12, // E6 O
- 0x08, // E8 E
- 0x00, // EA
- 0x00, // EC
- 0x0C, // EE I
- 0x1A, // F0 W
- 0x00, // F2
- 0x53, // F4 Numlock
- 0x13, // F6 P
- 0x14, // F8 Q
- 0x00, // FA
- 0x00, // FC
- 0x18, // FE U
-};
-
-
-// IR Keyboard State
-uint8_t _state = 0;
-uint16_t _code = 0;
-uint8_t _wt_keys[6] = {0};
-uint8_t _wt_expire[6] = {0};
-uint8_t _wt_modifiers = 0;
-
-static
-uint8_t parity_check(uint8_t k)
-{
- uint8_t c;
- uint8_t v = k;
- for (c = 0; v; c++)
- v &= v-1;
- return (c & 1) ? k : 0;
-}
-
-// make a mask from modifier keys
-static
-uint8_t ctrl_mask(uint8_t k)
-{
- switch (k) {
- case 0x38: return KEY_MOD_LCTRL;
- case 0x8C: return KEY_MOD_LSHIFT;
- case 0x18: return KEY_MOD_LALT;
- case 0xD4: return KEY_MOD_LGUI;
- case 0x2A: return KEY_MOD_RCTRL;
- case 0x4C: return KEY_MOD_RSHIFT;
- case 0x14: return KEY_MOD_RALT;
- case 0x62: return KEY_MOD_RGUI;
- }
- return 0;
-}
-
-// update state of held keys
-// produce a hid keyboard record
-int get_hid_webtv(uint8_t* dst)
-{
- bool dirty = false;
- uint8_t k = parity_check(_keyUp);
- _keyUp = 0;
- if (k) {
- _wt_modifiers &= ~ctrl_mask(k);
- for (int i = 0; i < 6; i++) {
- if (_wt_keys[i] == k) {
- _wt_expire[i] = 1; // key will expire this frame
- break;
- }
- }
- }
-
- k = parity_check(_keyDown);
- _keyDown = 0;
- if (k) {
- _wt_modifiers |= ctrl_mask(k);
-
- // insert key into list of pressed keys
- int j = 0;
- for (int i = 0; i < 6; i++) {
- if ((_wt_keys[i] == 0) || (_wt_expire[i] == 0) || (_wt_keys[i] == k)) {
- j = i;
- break;
- }
- if (_wt_expire[i] < _wt_expire[j])
- j = i;
- }
- if (_wt_keys[j] != k) {
- _wt_keys[j] = k;
- dirty = true;
- }
- _wt_expire[j] = 8; // key will be down for ~130ms or 8 frames
- }
-
- // generate hid keyboard events if anything was pressed or changed...
- // A1 01 mods XX k k k k k k
- dst[0] = 0xA1;
- dst[1] = 0x01;
- dst[2] = _wt_modifiers;
- dst[3] = 0;
- int j = 0;
- for (int i = 0; i < 6; i++) {
- dst[4+i] = 0;
- if (_wt_expire[i]) {
- if (!--_wt_expire[i])
- dirty = true;
- }
- if (_wt_expire[i] == 0) {
- _wt_keys[i] = 0;
- } else {
- dst[4 + j++] = _ir2scancode[_wt_keys[i] >> 1];
- }
- }
- return dirty ? 10 : 0;
-}
-
-// WebTV UART like keyboard protocol
-// 3.25 bit 0 start preamble the 19 bits
-// 10 bit code for keyup, keydown, all keys released etc
-// 7 bit keycode + parity bit.
-//
-
-#define KEYDOWN 0x4A
-#define KEYUP 0x5E
-void IRAM_ATTR ir_webtv(uint8_t t, uint8_t v)
-{
- if (_state == 0)
- {
- if (WT_PREAMBLE(t) && (v == 0)) // long 0, rising edge of start bit
- _state = 1;
- }
- else if (_state == 1)
- {
- _state = (SHORTBIT(t) && (v == 1)) ? 2 : 0;
- }
- else
- {
- t += BAUDB>>1;
- uint8_t bits = _state-2;
- while ((t > BAUDB) && (bits < 16))
- {
- t -= BAUDB;
- _code = (_code << 1) | v;
- bits++;
- }
- if (bits == 16)
- {
- v = t <= BAUDB;
- uint8_t md = _code >> 8;
- _code |= v; // Low bit of code is is parity
- if (md == KEYDOWN)
- _keyDown = _code;
- else if (md == KEYUP)
- _keyUp = _code;
- _state = 0; // got one
- return;
- }
- _state = bits+2;
- }
-}
-#endif
-
-// called from interrupt
-void IRAM_ATTR ir_event(uint8_t t, uint8_t v)
-{
-#ifdef WEBTV_KEYBOARD
- ir_webtv(t,v);
-#endif
-#ifdef RETCON_CONTROLLER
- ir_retcon(t,v);
-#endif
-#ifdef APPLE_TV_CONTROLLER
- ir_apple(t,v);
-#endif
-#ifdef FLASHBACK_CONTROLLER
- ir_flashback(t,v);
-#endif
-}
-
-// called every frame from emu
-int get_hid_ir(uint8_t* dst)
-{
- int n = 0;
-#ifdef APPLE_TV_CONTROLLER
- if (n = get_hid_apple(dst))
- return n;
-#endif
-#ifdef RETCON_CONTROLLER
- if (n = get_hid_retcon(dst))
- return n;
-#endif
-#ifdef FLASHBACK_CONTROLLER
- if (n = get_hid_flashback(dst))
- return n;
-#endif
- #ifdef WEBTV_KEYBOARD
- return get_hid_webtv(dst);
- #endif
- return 0;
-}
-#endif
diff --git a/src/nintendo.h b/src/nintendo.h
new file mode 100644
index 0000000..ded6a51
--- /dev/null
+++ b/src/nintendo.h
@@ -0,0 +1,195 @@
+
+/*
+ ** Copyright (c) 2010-2020, Peter Barrett
+ ** Copyright (c) 2024, Michel Depeige
+ **
+ ** Permission to use, copy, modify, and/or distribute this software for
+ ** any purpose with or without fee is hereby granted, provided that the
+ ** above copyright notice and this permission notice appear in all copies.
+ **
+ ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
+ ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ ** SOFTWARE.
+ */
+
+#ifndef __NINTENDO_H__
+#define __NINTENDO_H__
+
+#undef NES_CONTROLLER
+#define SNES_CONTROLLER
+
+/*
+ * ^^ choose either NES or SNES above
+ *
+ * inspired from Peter's work, and mixed with code I had around + CornN64 commits:
+ * - 8bdbe1c0ff234d1721ffe48472712b58b7f3683f
+ * - 937a9567224ab981d41fef5ba0e9ddcf19758996
+ *
+ * code only uses ESP-IDF, no Arduinism
+ *
+ * ADBSRC switch set if the classic controller is player A (0, pulled down) or B (1)
+ * the yellow LED on pin 26 is used as a controller activity LED by calling esp-idf stuff directly
+ * pin 26 is DAC channel 2 but it isn't used nor enabled so it works
+ */
+
+#define GPIO_YELLOWLED 26
+#define GPIO_ADBSRC 32
+
+/* how many buttons to poll */
+#ifdef NES_CONTROLLER
+#define NC_CLOCKS 8
+#endif
+#ifdef SNES_CONTROLLER
+#define NC_CLOCKS 12
+#endif
+
+/*
+ * this is added because poking into ESP-IDF is too fast. We are not letting
+ * enough time for the controller shift register and/or the main loop in the menu
+ */
+#define NC_DELAY 8
+
+/* globals for controller memory */
+uint16_t nc_joy[2] = {0};
+uint16_t nc_joy_last[2] = {0};
+
+/* internal functions */
+static void nc_set(int i, int m) {
+ uint8_t b = 0;
+
+ /* do not store if directional pad combinaisons are impossible */
+ if ((m & (GENERIC_LEFT | GENERIC_RIGHT)) == (GENERIC_LEFT | GENERIC_RIGHT))
+ b++; // bogus?
+ if ((m & (GENERIC_UP | GENERIC_DOWN)) == (GENERIC_UP | GENERIC_DOWN))
+ b++; // bogus?
+ if (b) {
+ printf("bogus:%04X\n",m);
+ return;
+ }
+
+ nc_joy[i] = m;
+}
+
+static int get_hid_nc(uint8_t* dst) {
+ /* send only HID event if something changes */
+ if ((nc_joy[0] != nc_joy_last[0]) || (nc_joy[1] != nc_joy_last[1])) {
+ nc_joy_last[0] = nc_joy[0];
+ nc_joy_last[1] = nc_joy[1];
+ dst[0] = 0xA1;
+ dst[1] = 0x42;
+ dst[2] = nc_joy[0];
+ dst[3] = nc_joy[0] >> 8;
+ dst[4] = nc_joy[1];
+ dst[5] = nc_joy[1] >> 8;
+ return 6;
+ }
+ return 0;
+}
+
+/*************************************
+ * Classic hard wired NES controller *
+ *************************************/
+enum NES_button {
+ NES_A = 0,
+ NES_B = 1,
+ NES_SELECT = 2,
+ NES_START = 3,
+ NES_UP = 4,
+ NES_DOWN = 5,
+ NES_LEFT = 6,
+ NES_RIGHT = 7,
+};
+
+/***************************************
+ * Classic hard wired SNES controllers *
+ ***************************************/
+enum SNES_button {
+ SNES_B = 0,
+ SNES_Y = 1,
+ SNES_SELECT = 2,
+ SNES_START = 3,
+ SNES_UP = 4,
+ SNES_DOWN = 5,
+ SNES_LEFT = 6,
+ SNES_RIGHT = 7,
+ SNES_A = 8,
+ SNES_X = 9,
+ SNES_L = 10,
+ SNES_R = 11,
+};
+
+/* called every frame from emu */
+int IRAM_ATTR get_hid_nintendo(uint8_t* dst) {
+ uint16_t buttons = 0;
+ uint16_t k = 0;
+
+ gpio_set_level((gpio_num_t)NES_CTRL_LATCH, 1);
+ ets_delay_us(NC_DELAY);
+ gpio_set_level((gpio_num_t)NES_CTRL_LATCH, 0);
+ ets_delay_us(NC_DELAY);
+
+ for (int i = 0; i < NC_CLOCKS; i++) {
+ buttons |= gpio_get_level((gpio_num_t)NES_CTRL_DAT) << i;
+ gpio_set_level((gpio_num_t)NES_CTRL_CLK, 0);
+ ets_delay_us(NC_DELAY);
+ gpio_set_level((gpio_num_t)NES_CTRL_CLK, 1);
+ ets_delay_us(NC_DELAY);
+ }
+
+#ifdef SNES_CONTROLLER
+ /* on NES, menu is LEFT (directional pad) + SELECT */
+ if ((buttons & ((1<<NES_LEFT) | (1<<NES_SELECT)))==0) {
+ k |= GENERIC_OTHER;
+ }
+ else {
+ if ((buttons & (1<<NES_UP))==0) k |= GENERIC_UP;
+ if ((buttons & (1<<NES_DOWN))==0) k |= GENERIC_DOWN;
+ if ((buttons & (1<<NES_LEFT))==0) k |= GENERIC_LEFT;
+ if ((buttons & (1<<NES_RIGHT))==0) k |= GENERIC_RIGHT;
+ if ((buttons & (1<<NES_A))==0) k |= GENERIC_FIRE | GENERIC_FIRE_A;
+ if ((buttons & (1<<NES_B))==0) k |= GENERIC_FIRE_B;
+ if ((buttons & (1<<NES_START))==0) k |= GENERIC_START;
+ if ((buttons & (1<<NES_SELECT))==0) k |= GENERIC_SELECT;
+ }
+#endif
+#ifdef SNES_CONTROLLER
+ /* on SNES, menu is LEFT + RIGHT buttons */
+ if ((buttons & ((1<<SNES_L) | (1<<SNES_R)))==0) {
+ k |= GENERIC_OTHER;
+ }
+ else {
+ if ((buttons & (1 << SNES_UP)) == 0) k |= GENERIC_UP;
+ if ((buttons & (1 << SNES_DOWN)) == 0) k |= GENERIC_DOWN;
+ if ((buttons & (1 << SNES_LEFT)) == 0) k |= GENERIC_LEFT;
+ if ((buttons & (1 << SNES_RIGHT)) == 0) k |= GENERIC_RIGHT;
+ if ((buttons & (1 << SNES_A)) == 0) k |= GENERIC_FIRE | GENERIC_FIRE_A;
+ if ((buttons & (1 << SNES_B)) == 0) k |= GENERIC_FIRE_B;
+ if ((buttons & (1 << SNES_START)) == 0) k |= GENERIC_START;
+ if ((buttons & (1 << SNES_SELECT)) == 0) k |= GENERIC_SELECT;
+ }
+#endif
+
+ /* Quack hard wired player A or B */
+ if (gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 0) {
+ nc_set(0,k); // no repeat period
+ nc_set(1,0); // no repeat period
+ }
+ else {
+ nc_set(0,0); // no repeat period
+ nc_set(1,k); // no repeat period
+ }
+
+ //printf("NC:\t%04X, %X\n", buttons, k);
+ if (k && buttons)
+ gpio_set_level((gpio_num_t)GPIO_YELLOWLED, 1);
+ else
+ gpio_set_level((gpio_num_t)GPIO_YELLOWLED, 0);
+
+ return get_hid_nc(dst);
+}
+#endif