├── BMC64 ├── CD32ControllerUSB.ino.with_bootloader.bin ├── README.md └── USBdescriptor.md ├── C64_1351_Mouse ├── README.md ├── c64_usb_mouse-debug.ino ├── c64_usb_mouse.ino └── c64_usb_mouse_paddles.ino ├── C64_4joy_adapter ├── 4joy_adapter.ino ├── README.md └── old │ ├── 4joy_adapter_old.ino │ ├── 4joy_adapter_old2.ino │ ├── 4joy_adapter_old3.ino │ ├── interrupt_test.ino │ ├── old3.txt │ ├── stuff.ino │ └── temp.ino ├── C64_joystick_atmelstudio ├── CLASS_JOYSTICK1.hex └── README.md ├── C64_keyboard ├── C64-JoyKEY.sc ├── C64_joystick.sc ├── C64_matrix.sc ├── README.md ├── Soarer_controller_for_C64.jpg └── theC64-sym-CLASSIC.vkm ├── Images ├── Arduino_ProMicro.jpg ├── Levelconverter_with_AMS1117.jpg ├── USB_Host_Shield_DuinoFun_UHS_mini_v2.jpg ├── Windows_Game_Controller_properties.jpg ├── ps2-keyboard-adapter.jpg ├── sega_genesis_adapter.jpg ├── self_made_NES_connector_arrangement.jpg ├── usb-shield-pinout.jpg └── x-arcade-dual-joystick.jpg ├── Keyboard_PS2 ├── PS2Keyboard_mcgurk.zip ├── README.md └── RetroJoystickAdapter_PS2-keyboard.ino ├── PS2_Soarer_Converter ├── README.md ├── Soarer_Converter_v1.10.zip ├── Soarer_Converter_v1.12_update.zip ├── arduino_pro_micro_soarer.jpg ├── empty.txt ├── rawhid.dll ├── scas.exe ├── scwr.exe ├── xarcade.txt ├── xarcade_bmc64.txt └── xarcade_mister.txt ├── README.md ├── RetroJoystickAdapter.ino ├── RetroJoystickAdapter_Atari.ino ├── RetroJoystickAdapter_Megadrive.ino ├── RetroJoystickAdapter_N64.ino ├── RetroJoystickAdapter_Playstation.ino ├── RetroJoystickAdapter_WiiExtension.ino ├── Tutorial ├── JoystickBlink.ino ├── README.md ├── SimpleAtariExample.ino └── SimpleAtariExample_keyboard.ino ├── Wii_Extension_debug.ino ├── X-Arcade ├── README.md ├── x-arcade.ino └── x-arcade_c64.ino ├── XBox360_XInput ├── README.md ├── RetroJoystickAdapter_Playstation_XB360 (old).ino ├── RetroJoystickAdapter_PsxNewLib_NintendoEtensionCtrl_B360.ino └── RetroJoystickAdapter_PsxNewLib_XB360.ino ├── atari ├── Hardware_Atari-SMS-Genesis.jpg └── README.md ├── boards.txt ├── megadrive ├── README.md ├── atmega_solded.jpg ├── atmega_solded_back.jpg ├── atmega_testing.jpg └── pinout.jpg ├── nes ├── README.md └── nes.png ├── playstation ├── README.md └── Sony_Playstation_Multitap.jpg └── test ├── README.md ├── RetroJoystickAdapter-2xNES.ino ├── RetroJoystickAdapter-DualShock.ino ├── RetroJoystickAdapter-NES.ino ├── RetroJoystickAdapter-SegaGenesis.ino ├── RetroJoystickAdapter-SegaGenesisKonami.ino └── RetroJoystickAdapter-psx.ino /BMC64/CD32ControllerUSB.ino.with_bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/BMC64/CD32ControllerUSB.ino.with_bootloader.bin -------------------------------------------------------------------------------- /BMC64/README.md: -------------------------------------------------------------------------------- 1 | # Objective: USB-adapter for Atari Joysticks for BMC64 with +5V and two fire support 2 | 3 | ## TL;DR 4 | - Build hardware: https://github.com/MiSTer-devel/Retro-Controllers-USB-MiSTer/tree/master/CD32ControllerUSB 5 | - (if you are not going to use CD32-controller, you can leave 220Ω resistor out and you don't have to connect pin5 from D9-connector) 6 | - Get avrdude.exe (if you have installed Arduino IDE, you already have that) 7 | - Download [CD32ControllerUSB.ino.with_bootloader.bin](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/refs/heads/master/BMC64/CD32ControllerUSB.ino.with_bootloader.bin) 8 | - Check what COM-port you have and flash firmware to Arduino Pro Micro from PowerShell: 9 | ``` 10 | & "$ENV:LOCALAPPDATA\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17\bin\avrdude.exe" -C "$ENV:LOCALAPPDATA\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17\etc\avrdude.conf" -v -patmega32u4 -c avr109 -U flash:w:"CD32ControllerUSB.ino.with_bootloader.bin":r -P com7 11 | ``` 12 | - If CDC is already disabled, you have to put Arduino flashing mode manually:
13 | Connect RST to GND couple of times to get Arduino Pro Micro to programming mode. Notice that COM-port is different in programming mode in Windows. Also notice that programming mode is available only couple of seconds from reset, so you have to time it right. 14 | 15 | ## Compiling yourself (works 31.3.2025 with Arduino IDE 2.3.4) 16 | - Install Arduino IDE 17 | - Download Arduino IDE project files from here: https://github.com/MiSTer-devel/Retro-Controllers-USB-MiSTer/tree/master/CD32ControllerUSB 18 | - Add `#define CDC_DISABLED` beginning of this file: %LocalAppData%\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino\USBDesc.h 19 | 20 | 21 | ## I'm gathering stuff for BMC64 emulator USB input devices here. 22 | - BMC64/theC64maxi/theVICmaxi compatible keyboard from Commodore keyboard: 23 | https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/tree/master/C64_keyboard 24 | - Second fire uses pin 9: https://wiki.icomp.de/wiki/DE-9_Joystick 25 | - https://github.com/MiSTer-devel/Retro-Controllers-USB-MiSTer/tree/master/CD32ControllerUSB 26 | - https://github.com/MickGyver/DaemonBite-CD32-USB 27 | - https://github.com/rainisto/arcade2usb-converter 28 | ``` 29 | If you use Arduino IDE, CDC device must be disabled or device doesn't work. 30 | 31 | Disabling CDC (IMPORTANT!): 32 | (works 31.3.2025 with Arduino IDE 2.3.4) 33 | C:\Users\[USER]\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino\USBDesc.h 34 | Add this line to start of file: 35 | #define CDC_DISABLED 36 | 37 | I used this: 38 | Adapter that works with original CD32-controller (works with normal joysticks too with 1 or 2 fires): 39 | https://github.com/MiSTer-devel/Retro-Controllers-USB-MiSTer/tree/master/CD32ControllerUSB 40 | Pinout and connections is front of source file: 41 | https://github.com/MiSTer-devel/Retro-Controllers-USB-MiSTer/blob/master/CD32ControllerUSB/CD32ControllerUSB.ino 42 | 43 | Another version of CD32ControllerUSB. Some differences, but I don't know if they are important: 44 | https://github.com/MickGyver/DaemonBite-CD32-USB 45 | Pinout and connections is front of source file: 46 | https://github.com/MickGyver/DaemonBite-CD32-USB/blob/master/CD32ControllerUSB/CD32ControllerUSB.ino 47 | (might support two controllers with one Arduino Pro Micro, but I haven't tested that with BMC64) 48 | 49 | Another possibility is use theC64mini (this adapter works with theC64mini too): 50 | https://github.com/rainisto/arcade2usb-converter 51 | Pinout and connections is front of source file: 52 | https://github.com/rainisto/arcade2usb-converter/blob/master/source/c64mini-arcade2usb-converter/c64mini-arcade2usb-converter.ino 53 | 54 | ``` 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /C64_1351_Mouse/README.md: -------------------------------------------------------------------------------- 1 | ## USB mouse -> 1351 (C64) mouse adapter (+paddles) 2 | 3 | #### Requirements 4 | 5 | - Arduino Pro Micro 16MHz 5V (or Arduino Uno 16MHz 5V) 6 | - USB Host Shield MAX3421EE (USB Host Shield Library 2.0 from Arduino IDE Library Manager) 7 | - 2pcs 10k resistors 8 | - Level shifter for 6 pins 9 | 10 | #### Pin 9 and 10 needed for TIMER1 - Move SS and INT of USB host shield library away 11 | 12 | C:\Users\xxxxx\Documents\Arduino\libraries\USB_Host_Shield_Library_2.0\UsbCore.h: 13 | ``` 14 | //typedef MAX3421e MAX3421E; // default pin asignments 15 | //Arduino Pro Micro SS=A0(P18), INT=A1(P19): 16 | typedef MAX3421e MAX3421E; 17 | //Arduino Uno SS=A0(P14), INT=A1(P15): 18 | //typedef MAX3421e MAX3421E; 19 | ``` 20 | 21 | #### Joystick port -> Arduino 22 | - 9 (POTX) -> 10k resistor -> 9 (OC1A) (brown) 23 | - 9 (POTX) -> Arduino Pro Micro: 4, Arduino Uno: 8 (ICP1) (white) 24 | - 5 (POTY) -> 10k resistor -> 10 (OC1B) (orange) 25 | - 6 (FIRE, left mouse button) -> 5 (yellow) 26 | - 1 (UP, right mouse button) -> 6 (blue) 27 | - 8 GND -> GND (black) 28 | - 7 5V -> 5V (red) 29 | ##### +Paddles: 30 | - 3 (LEFT) (Paddle 1 fire) -> 7 (grey) 31 | - 4 (RIGHT) (Paddle 2 fire) -> 8 (purple) 32 | 33 | (unconnected: 2 DOWN (green)) 34 | 35 | #### USB Host Shield (3.3V) -> Arduino 36 | - SS -> A0 (Arduino Pro Micro), A0 (Arduino Uno) (check UsbCore.h) (3.3V!) 37 | - INT -> A1 (Arduino Pro Micro), A1 (Arduino Uno) (check UsbCore.h) (3.3V!) 38 | - MOSI -> 16 (Arduino Pro Micro), 11 (Arduino Uno) (3.3V!) 39 | - MISO -> 14 (Arduino Pro Micro), 12 (Arduino Uno) (3.3V!) 40 | - CLK -> 15 (Arduino Pro Micro), 13 (Arduino Uno) (3.3V!) 41 | - RST -> RST (3.3V!) (Pro Micro: beware of GND in place of RST!) 42 | - GND -> GND 43 | - VCC -> 3.3V 44 | - VBUS -> 5V (cut trace from VBUS-pad to resistor!) 45 | 46 | !: MOSI, MISO and CLK pins are wrongly marked in USB Host Shield DuinoFun UHS mini v2 -module! 47 | 48 | #### Links 49 | - http://asdasd.rpg.fi/~svo/%5bm%5douse/ 50 | - http://www.zimmers.net/anonftp/pub/cbm/documents/projects/interfaces/mouse/Mouse.html 51 | - https://www.google.com/patents/US4886941 52 | - https://ist.uwaterloo.ca/~schepers/MJK/pics/joyports.gif 53 | - Trace cut from USB Host Shield mini: https://geekhack.org/index.php?topic=80421.0 54 | - https://gammon.com.au/interrupts 55 | - http://www.avrbeginners.net/architecture/timers/timers.html 56 | - https://github.com/felis/USB_Host_Shield_2.0/blob/master/avrpins.h 57 | - https://github.com/felis/USB_Host_Shield_2.0/blob/master/UsbCore.h 58 | 59 | ### Hardware 60 | 61 | I used Arduino Pro Micro and module which have AMS1117 3.3V regulator and 8 bidirectional voltage converters. 62 | 63 | #### Arduino 64 | ![Arduino Pro Micro](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/master/Images/Arduino_ProMicro.jpg) 65 | 66 | #### USB Host Shield 67 | ![USB Host Shield DuinoFun UHS mini v2](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/master/Images/USB_Host_Shield_DuinoFun_UHS_mini_v2.jpg) 68 | 69 | #### Level converter with regulator 70 | ![Level converter with AMS1117](https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/master/Images/Levelconverter_with_AMS1117.jpg) 71 | 72 | #### USB-shield pinout 73 | ![USB-shield pinout](https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/master/Images/usb-shield-pinout.jpg) 74 | -------------------------------------------------------------------------------- /C64_1351_Mouse/c64_usb_mouse-debug.ino: -------------------------------------------------------------------------------- 1 | #define USBHOST 2 | 3 | #ifdef USBHOST 4 | #include 5 | #include 6 | #endif 7 | 8 | #define POTSENSE 4 //ICP1 (Arduino Pro Micro: pin4, Arduino Uno: pin8) 9 | 10 | #define POTX 9 ///< X-line, also OC1A 11 | #define POTY 10 ///< Y-line, also OC1B 12 | #define LBTN 5 ///< Joystick FIRE switch 13 | #define RBTN 6 ///< Joystick UP switch 14 | 15 | //#define DEBUG 16 | 17 | int16_t dx=0; 18 | int16_t dy=0; 19 | uint8_t buttons=0; 20 | 21 | uint8_t update = 0; 22 | 23 | #ifdef USBHOST 24 | class MouseRptParser : public MouseReportParser { 25 | protected: 26 | void OnMouseMove(MOUSEINFO *mi); 27 | void OnLeftButtonUp(MOUSEINFO *mi); 28 | void OnLeftButtonDown(MOUSEINFO *mi); 29 | void OnRightButtonUp(MOUSEINFO *mi); 30 | void OnRightButtonDown(MOUSEINFO *mi); 31 | void OnMiddleButtonUp(MOUSEINFO *mi); 32 | void OnMiddleButtonDown(MOUSEINFO *mi); 33 | }; 34 | void MouseRptParser::OnMouseMove(MOUSEINFO *mi) { 35 | #ifdef DEBUG 36 | Serial.print("dx="); 37 | Serial.print(mi->dX, DEC); 38 | Serial.print(" dy="); 39 | Serial.println(mi->dY, DEC); 40 | #endif 41 | dx=mi->dX; 42 | dy=mi->dY; 43 | update = 1; 44 | }; 45 | void MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi) { 46 | #ifdef DEBUG 47 | Serial.println("L Butt Up"); 48 | #endif 49 | buttons &= ~1; 50 | update = 1; 51 | }; 52 | void MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi) { 53 | #ifdef DEBUG 54 | Serial.println("L Butt Dn"); 55 | #endif 56 | buttons |= 1; 57 | update = 1; 58 | }; 59 | void MouseRptParser::OnRightButtonUp(MOUSEINFO *mi) { 60 | #ifdef DEBUG 61 | Serial.println("R Butt Up"); 62 | #endif 63 | buttons &= ~2; 64 | update = 1; 65 | }; 66 | void MouseRptParser::OnRightButtonDown(MOUSEINFO *mi) { 67 | #ifdef DEBUG 68 | Serial.println("R Butt Dn"); 69 | #endif 70 | buttons |= 2; 71 | update = 1; 72 | }; 73 | void MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi) { 74 | #ifdef DEBUG 75 | Serial.println("M Butt Up"); 76 | #endif 77 | }; 78 | void MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi) { 79 | #ifdef DEBUG 80 | Serial.println("M Butt Dn"); 81 | #endif 82 | }; 83 | 84 | USB Usb; 85 | USBHub Hub(&Usb); 86 | HIDBoot HidMouse(&Usb); 87 | 88 | MouseRptParser Prs; 89 | #endif 90 | 91 | static uint8_t potmouse_xcounter; ///< x axis counter 92 | static uint8_t potmouse_ycounter; ///< y axis counter 93 | 94 | static volatile uint16_t ocr1a_load; ///< precalculated OCR1A value (YPOT) 95 | static volatile uint16_t ocr1b_load; ///< precalculated OCR1B value (XPOT) 96 | 97 | void setup() { 98 | #ifdef DEBUG 99 | Serial.begin(115200); 100 | delay(200); 101 | Serial.println("Start"); 102 | Serial.flush(); 103 | #endif 104 | 105 | #ifdef USBHOST 106 | if (Usb.Init() == -1) { 107 | #ifdef DEBUG 108 | Serial.println("OSC did not start."); 109 | #endif 110 | } 111 | delay(200); 112 | HidMouse.SetReportParser(0, &Prs); 113 | #endif 114 | 115 | #ifdef DEBUG 116 | Serial.flush(); 117 | delay(200); 118 | #endif 119 | 120 | pinMode(LBTN, INPUT); pinMode(RBTN, INPUT); 121 | 122 | pinMode(POTX, OUTPUT); pinMode(POTY, OUTPUT); 123 | digitalWrite(POTX, HIGH); digitalWrite(POTY, HIGH); 124 | pinMode(POTSENSE, INPUT); // pullup off, hi-biased by OC1A 125 | 126 | potmouse_movt(0,0,0); 127 | 128 | startTimers(); 129 | 130 | #ifndef USBHOST 131 | TIMSK0 = 0; 132 | #endif 133 | 134 | } 135 | 136 | void loop() { 137 | #ifdef USBHOST 138 | Usb.Task(); 139 | if (update) { 140 | potmouse_movt(dx, dy, buttons); 141 | update = 0; 142 | } 143 | delayMicroseconds(200); 144 | #endif 145 | 146 | #ifndef USBHOST 147 | potmouse_movt(0, 0, buttons); 148 | #endif 149 | 150 | } 151 | 152 | volatile uint8_t counter = 0; 153 | volatile uint8_t upd = 0; 154 | 155 | void potmouse_movt(int16_t dx, int16_t dy, uint8_t button) { 156 | uint16_t a, b; 157 | 158 | #ifndef USBHOST 159 | if (upd) { 160 | potmouse_xcounter++; 161 | potmouse_ycounter++; 162 | upd = 0; 163 | } 164 | #endif 165 | 166 | potmouse_xcounter = (potmouse_xcounter + (dx/2)) & 0177; // modulo 128 167 | potmouse_ycounter = (potmouse_ycounter - (dy/2)) & 0177; 168 | 169 | //for testing 170 | //potmouse_xcounter = (millis()>>6) & 077; // modulo 64 171 | //potmouse_ycounter = (millis()>>6) & 077; 172 | 173 | (button & 001) ? pinMode(LBTN, OUTPUT) : pinMode(LBTN, INPUT); 174 | (button & 002) ? pinMode(RBTN, OUTPUT) : pinMode(RBTN, INPUT); 175 | 176 | // scale should be 2x here, but for this particular chip, 66 counts work better where 177 | // 64 counds should be. so 66/64=100/96 and times two 178 | //a = 320*2 + ((uint32_t)potmouse_xcounter)*200/fix; 179 | //b = 320*2 + ((uint32_t)potmouse_ycounter)*200/fix; 180 | //a = 320*200/fix + potmouse_xcounter*2; 181 | //b = 320*200/fix + potmouse_ycounter*2; 182 | a = 320*2 + potmouse_xcounter*2; 183 | b = 320*2 + potmouse_ycounter*2; 184 | 185 | ocr1a_load = a; 186 | ocr1b_load = b; 187 | } 188 | 189 | 190 | inline void startTimers() { 191 | #ifdef DEBUG 192 | Serial.println("startTimers"); Serial.flush(); 193 | #endif 194 | cli(); 195 | 196 | // Prepare TIMER1 197 | //TCCR1A = 0; 198 | 199 | // ICIE1: Timer/Counter Input Capture Interrupt Enable, ISR(TIMER1_CAPT_vect) 200 | // TOIE1: Timer/Counter Overflow Interrupt Enable 201 | TIMSK1 = _BV(ICIE1); // ICIE1: Timer/Counter1, Input Capture Interrupt Enable 202 | 203 | // Start timer1, Input Capture setup 204 | // ICNC1: Input Capture Noise Canceller (Bit 7 of register TCCR1B) 205 | // ICES1: Input Capture Edge Select (Bit 6 of register TCCR1B) 0 = FALLING, 1 = RISING 206 | // CS12, CS11, CS10: Set prescaler (CS11 TIMER1: F_CPU/8) 207 | TCCR1B = _BV(ICNC1) | _BV(CS11); 208 | //TCCR1B = _BV(CS11); 209 | 210 | TIFR1 = 0xff; // Clear all pending TIMER1 interrupt flags 211 | 212 | sei(); 213 | } 214 | 215 | 216 | 217 | ISR(TIMER1_CAPT_vect) { 218 | // Now we little after start of SID reading process 219 | // SID trigger pulse timer value is in ICR1 220 | 221 | uint16_t a = ICR1; 222 | 223 | #ifdef DEBUG 224 | Serial.println("TIMER1_CAPT_vect:"); 225 | #endif 226 | 227 | #ifndef USBHOST 228 | counter++; 229 | counter &= 63; 230 | if (counter == 0) upd = 1; 231 | #endif 232 | 233 | // clear OC1A/OC1B (9 and 10 to LOW): 234 | // 1. set output compare to clear OC1A/OC1B ("10" in table 37 on page 97) 235 | TCCR1A = _BV(COM1A1) | _BV(COM1B1); // Clear OC1A / OC1B on Compare Match (Set output to low level). 236 | // 2. force output compare to make it happen (doesn't raise interrupts) 237 | TCCR1C |= _BV(FOC1A) | _BV(FOC1B); // FOC1A / FOC1B Force Output Compare A and B (that are in register TCCR1C) 238 | 239 | // OCIE1A: Timer/Counter Output Compare Match Interrupt Enable A, ISR(TIMER1_COMPA_vect) // disable ICIE1, Input Capture Interrupt 240 | TIMSK1 = _BV(OCIE1A); 241 | 242 | // init the output compare values 243 | OCR1A = ocr1a_load + a; 244 | OCR1B = ocr1b_load + a; 245 | 246 | // Set OC1A/OC1B on Compare Match (Set output to high level) 247 | // WGM13:0 = 00, normal mode: count from BOTTOM to MAX 248 | TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0); // Set OC1A / OC1B on Compare Match (Set output to high level). 249 | 250 | #ifdef DEBUG 251 | Serial.print(c); Serial.print(" "); Serial.print(a); Serial.print(" "); Serial.println(b); 252 | Serial.flush(); 253 | #endif 254 | 255 | TIFR1 = 0xff; //clear all timer1 interrupt flags 256 | } 257 | 258 | ISR(TIMER1_COMPA_vect) { 259 | // now potx are sent. we don't know if poty is still in progress. 260 | // POTX is HIGH from OC1A TIMER1 compare match. POTY is ?. 261 | 262 | #ifdef DEBUG 263 | Serial.println("TIMER1_COMPA_vect"); Serial.flush(); 264 | #endif 265 | 266 | TIMSK1 = _BV(ICIE1); // ICIE1: Timer/Counter1, Input Capture Interrupt Enable // disable TIMER1 interrupts (Compare Match Interrupt A) 267 | TIFR1 = 0xff; //clear all timer1 interrupt flags 268 | } 269 | -------------------------------------------------------------------------------- /C64_1351_Mouse/c64_usb_mouse.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define POTSENSE 4 //ICP1 (Arduino Pro Micro: pin4, Arduino Uno: pin8) 5 | 6 | #define POTX 9 // POT AX 9, also OC1A 7 | #define POTY 10 // POT AY 5, also OC1B 8 | #define LBTN 5 // BTN 6, Joystick FIRE switch 9 | #define RBTN 6 // UP 1, Joystick UP switch 10 | 11 | int16_t dx = 0; 12 | int16_t dy = 0; 13 | uint8_t buttons = 0; 14 | 15 | uint8_t update = 1; 16 | 17 | class MouseRptParser : public MouseReportParser { 18 | protected: 19 | void OnMouseMove(MOUSEINFO *mi); 20 | void OnLeftButtonUp(MOUSEINFO *mi); 21 | void OnLeftButtonDown(MOUSEINFO *mi); 22 | void OnRightButtonUp(MOUSEINFO *mi); 23 | void OnRightButtonDown(MOUSEINFO *mi); 24 | void OnMiddleButtonUp(MOUSEINFO *mi); 25 | void OnMiddleButtonDown(MOUSEINFO *mi); 26 | }; 27 | void MouseRptParser::OnMouseMove(MOUSEINFO *mi) { 28 | dx=mi->dX; 29 | dy=mi->dY; 30 | update = 1; 31 | }; 32 | void MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi) { 33 | buttons &= ~1; 34 | update = 1; 35 | }; 36 | void MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi) { 37 | buttons |= 1; 38 | update = 1; 39 | }; 40 | void MouseRptParser::OnRightButtonUp(MOUSEINFO *mi) { 41 | buttons &= ~2; 42 | update = 1; 43 | }; 44 | void MouseRptParser::OnRightButtonDown(MOUSEINFO *mi) { 45 | buttons |= 2; 46 | update = 1; 47 | }; 48 | void MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi) { 49 | }; 50 | void MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi) { 51 | }; 52 | 53 | USB Usb; 54 | USBHub Hub(&Usb); 55 | HIDBoot HidMouse(&Usb); 56 | 57 | MouseRptParser Prs; 58 | 59 | 60 | static uint8_t potmouse_xcounter = 0; ///< x axis counter 61 | static uint8_t potmouse_ycounter = 0; ///< y axis counter 62 | 63 | static volatile uint16_t x; ///< precalculated OCR1A value (YPOT) 64 | static volatile uint16_t y; ///< precalculated OCR1B value (XPOT) 65 | 66 | void setup() { 67 | if (Usb.Init() == -1) { 68 | } 69 | delay(200); 70 | HidMouse.SetReportParser(0, &Prs); 71 | 72 | pinMode(LBTN, INPUT); pinMode(RBTN, INPUT); 73 | 74 | pinMode(POTX, OUTPUT); pinMode(POTY, OUTPUT); 75 | digitalWrite(POTX, HIGH); digitalWrite(POTY, HIGH); 76 | pinMode(POTSENSE, INPUT); // pullup off, hi-biased by OC1A 77 | 78 | TIMSK1 = _BV(ICIE1); // ICIE1: Timer/Counter1, Input Capture Interrupt Enable 79 | TCCR1B = _BV(ICNC1) | _BV(CS10); //CS10: No prescaler, ICNC1: Input Capture Noise Canceller 80 | } 81 | 82 | 83 | void loop() { 84 | Usb.Task(); 85 | if (update) { 86 | potmouse_xcounter = (potmouse_xcounter + (dx/2)) & 0177; // modulo 128 87 | potmouse_ycounter = (potmouse_ycounter - (dy/2)) & 0177; 88 | 89 | (buttons & 001) ? pinMode(LBTN, OUTPUT) : pinMode(LBTN, INPUT); 90 | (buttons & 002) ? pinMode(RBTN, OUTPUT) : pinMode(RBTN, INPUT); 91 | 92 | cli(); 93 | x = (320 + potmouse_xcounter)*16; //16 clock cycles = 1us 94 | y = (320 + potmouse_ycounter)*16; 95 | sei(); 96 | update = 0; 97 | } 98 | delayMicroseconds(100); 99 | } 100 | 101 | 102 | ISR(TIMER1_CAPT_vect) { // ICIE1 103 | // OC1A/OC1B -> LOW 104 | TCCR1A = _BV(COM1A1) | _BV(COM1B1); // Clear OC1A / OC1B on Compare Match (Set output to low level) 105 | TCCR1C |= _BV(FOC1A) | _BV(FOC1B); // FOC1A / FOC1B Force Output Compare A and B 106 | 107 | // init the output compare values 108 | OCR1A = ICR1 + x; //ICR1: Input Capture Register 109 | OCR1B = ICR1 + y; 110 | 111 | TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0); // Set OC1A / OC1B on Compare Match (Set output to high level). 112 | 113 | TIMSK1 = _BV(OCIE1A); // OCIE1A: Timer/Counter Output Compare Match Interrupt Enable A // disable other TIMER1 interrupts 114 | TIFR1 = 0xff; // Clear all pending TIMER1 interrupt flags 115 | } 116 | 117 | 118 | ISR(TIMER1_COMPA_vect) { // OCIE1A 119 | TIMSK1 = _BV(ICIE1); // ICIE1: Timer/Counter1, Input Capture Interrupt Enable // disable other TIMER1 interrupts 120 | TIFR1 = 0xff; // Clear all pending TIMER1 interrupt flags 121 | } 122 | -------------------------------------------------------------------------------- /C64_1351_Mouse/c64_usb_mouse_paddles.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define POTSENSE 4 //ICP1 (Arduino Pro Micro: pin4, Arduino Uno: pin8) 5 | 6 | #define POTX 9 // POT AX 9, also OC1A 7 | #define POTY 10 // POT AY 5, also OC1B 8 | #define LBTN 5 // BTN 6, Joystick FIRE switch 9 | #define RBTN 6 // UP 1, Joystick UP switch 10 | #define PDL1BTN 7 // LEFT 3, Paddle 1 FIRE switch 11 | #define PDL2BTN 8 // RIGHT 4, Paddle 2 FIRE switch 12 | 13 | int16_t dx = 0; 14 | int16_t dy = 0; 15 | uint8_t buttons = 0; 16 | 17 | uint8_t update = 1; 18 | 19 | class MouseRptParser : public MouseReportParser { 20 | protected: 21 | void OnMouseMove(MOUSEINFO *mi); 22 | void OnLeftButtonUp(MOUSEINFO *mi); 23 | void OnLeftButtonDown(MOUSEINFO *mi); 24 | void OnRightButtonUp(MOUSEINFO *mi); 25 | void OnRightButtonDown(MOUSEINFO *mi); 26 | void OnMiddleButtonUp(MOUSEINFO *mi); 27 | void OnMiddleButtonDown(MOUSEINFO *mi); 28 | }; 29 | void MouseRptParser::OnMouseMove(MOUSEINFO *mi) { 30 | dx=mi->dX; 31 | dy=mi->dY; 32 | update = 1; 33 | }; 34 | void MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi) { 35 | buttons &= ~1; 36 | update = 1; 37 | }; 38 | void MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi) { 39 | buttons |= 1; 40 | update = 1; 41 | }; 42 | void MouseRptParser::OnRightButtonUp(MOUSEINFO *mi) { 43 | buttons &= ~2; 44 | update = 1; 45 | }; 46 | void MouseRptParser::OnRightButtonDown(MOUSEINFO *mi) { 47 | buttons |= 2; 48 | update = 1; 49 | }; 50 | void MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi) { 51 | }; 52 | void MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi) { 53 | }; 54 | 55 | USB Usb; 56 | USBHub Hub(&Usb); 57 | HIDBoot HidMouse(&Usb); 58 | 59 | MouseRptParser Prs; 60 | 61 | 62 | static uint8_t potmouse_xcounter = 0; ///< x axis counter 63 | static uint8_t potmouse_ycounter = 0; ///< y axis counter 64 | 65 | static volatile uint16_t x; ///< precalculated OCR1A value (YPOT) 66 | static volatile uint16_t y; ///< precalculated OCR1B value (XPOT) 67 | 68 | void setup() { 69 | if (Usb.Init() == -1) { 70 | } 71 | delay(200); 72 | HidMouse.SetReportParser(0, &Prs); 73 | 74 | pinMode(LBTN, INPUT); pinMode(RBTN, INPUT); 75 | pinMode(PDL1BTN, INPUT); pinMode(PDL2BTN, INPUT); 76 | 77 | pinMode(POTX, OUTPUT); pinMode(POTY, OUTPUT); 78 | digitalWrite(POTX, HIGH); digitalWrite(POTY, HIGH); 79 | pinMode(POTSENSE, INPUT); // pullup off, hi-biased by OC1A 80 | 81 | TIMSK1 = _BV(ICIE1); // ICIE1: Timer/Counter1, Input Capture Interrupt Enable 82 | TCCR1B = _BV(ICNC1) | _BV(CS10); //CS10: No prescaler, ICNC1: Input Capture Noise Canceller 83 | } 84 | 85 | 86 | void loop() { 87 | Usb.Task(); 88 | if (update) { 89 | //potmouse_xcounter = (potmouse_xcounter + (dx/2)) & 0177; // modulo 128 90 | //potmouse_ycounter = (potmouse_ycounter - (dy/2)) & 0177; 91 | int16_t t1 = (potmouse_xcounter - (dx/2)); 92 | int16_t t2 = (potmouse_ycounter + (dy/2)); 93 | potmouse_xcounter = constrain(t1, 0, 255); 94 | potmouse_ycounter = constrain(t2, 0, 255); 95 | /*if (t1 < 0) t1 = 0; if (t1 > 255) t1 = 255; 96 | potmouse_xcounter = t1; //(potmouse_xcounter - (dx/2));// & 0xff; // modulo 256 97 | if (t2 < 0) t2 = 0; if (t2 > 255) t2 = 255; 98 | potmouse_ycounter = t2; //(potmouse_ycounter + (dy/2));// & 0xff;*/ 99 | 100 | //(buttons & 001) ? pinMode(LBTN, OUTPUT) : pinMode(LBTN, INPUT); 101 | //(buttons & 002) ? pinMode(RBTN, OUTPUT) : pinMode(RBTN, INPUT); 102 | (buttons & 001) ? pinMode(PDL1BTN, OUTPUT) : pinMode(PDL1BTN, INPUT); 103 | (buttons & 002) ? pinMode(PDL2BTN, OUTPUT) : pinMode(PDL2BTN, INPUT); 104 | 105 | cli(); 106 | //x = (320 + potmouse_xcounter)*16; //16 clock cycles = 1us 107 | //y = (320 + potmouse_ycounter)*16; 108 | //x = (192 + potmouse_xcounter)*16; //16 clock cycles = 1us 109 | //y = (192 + potmouse_ycounter)*16; 110 | x = (256 + potmouse_xcounter)*16; //16 clock cycles = 1us 111 | y = (256 + potmouse_ycounter)*16; 112 | sei(); 113 | update = 0; 114 | } 115 | delayMicroseconds(100); 116 | } 117 | 118 | 119 | ISR(TIMER1_CAPT_vect) { // ICIE1 120 | // OC1A/OC1B -> LOW 121 | TCCR1A = _BV(COM1A1) | _BV(COM1B1); // Clear OC1A / OC1B on Compare Match (Set output to low level) 122 | TCCR1C |= _BV(FOC1A) | _BV(FOC1B); // FOC1A / FOC1B Force Output Compare A and B 123 | 124 | // init the output compare values 125 | OCR1A = ICR1 + x; //ICR1: Input Capture Register 126 | OCR1B = ICR1 + y; 127 | 128 | TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0); // Set OC1A / OC1B on Compare Match (Set output to high level). 129 | 130 | TIMSK1 = _BV(OCIE1A); // OCIE1A: Timer/Counter Output Compare Match Interrupt Enable A // disable other TIMER1 interrupts 131 | TIFR1 = 0xff; // Clear all pending TIMER1 interrupt flags 132 | } 133 | 134 | 135 | ISR(TIMER1_COMPA_vect) { // OCIE1A 136 | TIMSK1 = _BV(ICIE1); // ICIE1: Timer/Counter1, Input Capture Interrupt Enable // disable other TIMER1 interrupts 137 | TIFR1 = 0xff; // Clear all pending TIMER1 interrupt flags 138 | } 139 | -------------------------------------------------------------------------------- /C64_4joy_adapter/4joy_adapter.ino: -------------------------------------------------------------------------------- 1 | // Protovision 4 player interface / Classical Games adapter (1997) 2 | // Arduino Pro Micro 3 | // https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters 4 | // https://www.protovision.games/hardw/build4player.php?language=en 5 | // http://cloud.cbm8bit.com/penfold42/joytester.zip 6 | // https://arduino.stackexchange.com/questions/8758/arduino-interruption-on-pin-change/8926 7 | // http://www.nongnu.org/avr-libc/user-manual/inline_asm.html 8 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf 9 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf 10 | // http://www.pighixxx.net/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png 11 | // https://opencircuit.shop/ProductInfo/1000378/ATmega32U4-Datasheet.pdf 12 | // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Pro_Micro_v13b.pdf 13 | 14 | // TXD (INT3,PD3) and RXD (INT2,PD2) to userport L. 15 | // INT2(RXD) is used for rising edge and INT3(TXD) for falling edge. 16 | // Interrupts takes only less than 1us to change output port state after select-signal. 17 | 18 | // Joystick port 3 19 | // GND = GND (8) 20 | #define up1 (~PD & _BV(7)) // 6,PD7 = (1) 21 | #define down1 (~PC & _BV(6)) // 5,PC6 = (2) 22 | #define left1 (~PD & _BV(4)) // 4,PD4 = (3) 23 | #define right1 (~PD & _BV(0)) // 3,PD0 = (4) 24 | #define fire1 (~PD & _BV(1)) // 2,PD1 = (6) 25 | 26 | // Joystick port 4 27 | // GND = GND (8) 28 | #define up2 (~PF & _BV(7)) // A0,PF7 = (1) 29 | #define down2 (~PF & _BV(6)) // A1,PF6 = (2) 30 | #define left2 (~PF & _BV(5)) // A2,PF5 = (3) 31 | #define right2 (~PF & _BV(4)) // A3,PF4 = (4) 32 | #define fire2 (~PE & _BV(6)) // 7,PE6 = (6) 33 | 34 | // Arduino <-> Userport 35 | // VCC = +5V (2) 36 | // GND = GND (A) 37 | #define upC 1 // 15,PB1 = PB0 (C) 38 | #define downC 3 // 14,PB3 = PB1 (D) 39 | #define leftC 2 // 16,PB2 = PB2 (E) 40 | #define rightC 6 // 10,PB6 = PB3 (F) 41 | #define fire1C 4 // 8,PB4 = PB4 (H) 42 | #define fire2C 5 // 9,PB5 = PB5 (J) 43 | #define selectC (PIND & _BV(2)) // RXD,PD2(INT2)+TXD,PD3(INT3) = PB7 (L) 44 | 45 | // LEDS (inverted): 46 | // RX = D17,PB0 47 | // TX = -,PD5 48 | 49 | // GPIOR2 contains data space address to GPIOR0 or GPRIO1. 50 | 51 | ISR(INT2_vect, ISR_NAKED) { // rising edge, output joystick 3 52 | asm volatile( 53 | " push r0 \n" // 2 cycles 54 | " in r0, 0x3f \n" // 0x3f = SREG // 1 cycle 55 | " push r24 \n" // 2 cycles 56 | " in r24, %[gpio] \n" // 1 cycle 57 | " out %[pin], r24 \n" // 1 cycle 58 | " ldi r24, 0x3e \n" //0x3e = gpior0 data space 59 | " out 0x2b, r24 \n" //0x2b = gpior2 io space 60 | " pop r24 \n" 61 | " out 0x3f, r0 \n" // 0x3f = SREG 62 | " pop r0 \n" 63 | " reti \n" 64 | //" rjmp INT2_vect_part_2 \n" // go to part 2 for required prologue and epilogue 65 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR0))); 66 | } 67 | 68 | // interrupt preparation minimum 5 cycles 69 | // jump to interrupt routine 3 cycles 70 | // 7 cycles to the point where out command is ready 71 | // = 5+3+7 = 15 cycles (62,5ns * 15 = 0,9375us). Under 1 6502 cycle. 72 | // 6502 takes 4 cycles for sta $dd01 and 4 cycles for lda $dd01 73 | 74 | //ISR(INT2_vect_part_2) { GPIOR2 = &GPIOR0; } 75 | 76 | ISR(INT3_vect, ISR_NAKED) { // falling edge, output joystick 4 77 | asm volatile( 78 | " push r0 \n" 79 | " in r0, 0x3f \n" // 0x3f = SREG 80 | " push r24 \n" 81 | " in r24, %[gpio] \n" 82 | " out %[pin], r24 \n" 83 | " ldi r24, 0x4a \n" //0x4a = gpior1 data space 84 | " out 0x2b, r24 \n" //0x2b = gpior2 io space 85 | " pop r24 \n" 86 | " out 0x3f, r0 \n" // 0x3f = SREG 87 | " pop r0 \n" 88 | " reti \n" 89 | //" rjmp INT3_vect_part_2 \n" // go to part 2 for required prologue and epilogue 90 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR1))); 91 | } 92 | 93 | //ISR(INT3_vect_part_2) { GPIOR2 = &GPIOR1; } 94 | 95 | void setup() { 96 | DDRB = 0xff; PORTB = 0xff; //all PB-ports are outputs and high (0xff = zero state, because signals are inverted) 97 | DDRF = 0; PORTF = 0xff; // all PF-ports are inputs with pullups 98 | DDRD = B00100000; PORTD = B11110011; // all PD-ports are inputs (except PD5) with pullups (PD2,PD3 without pullup) 99 | pinMode(5, INPUT_PULLUP); // pin5 (PC6) is input 100 | pinMode(7, INPUT_PULLUP); // pin7 (PE6) is input 101 | 102 | if (selectC) GPIOR2 = &GPIOR0; else GPIOR2 = &GPIOR1; 103 | GPIOR0 = 0xff; GPIOR1 = 0xff; // start from zero state (signals are inverted) 104 | 105 | TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis()/micros() update ISR) 106 | 107 | EICRA = B10110000; // INT2 – rising edge on RXD (Bxx11xxxx), INT3 - falling edge on TXD (B10xxxxxx) 108 | EIMSK = B1100; // enable INT2 (Bx1xx) and INT3 (B1xxx) 109 | 110 | //Serial.begin(115200); //Can't use serial port; RX and TX is dedicated for interrupts 111 | //PORTD &= ~_BV(5); // TX-LED on 112 | 113 | } 114 | 115 | void loop() { 116 | uint8_t PF, PD, PC, PE; 117 | PF = PINF; PD = PIND; PC = PINC; PE = PINE; 118 | uint8_t joy1 = 0xff; uint8_t joy2 = 0xff; // all signals are inverted 119 | if (up1) bitClear(joy1,upC); 120 | if (down1) bitClear(joy1,downC); 121 | if (left1) bitClear(joy1,leftC); 122 | if (right1) bitClear(joy1,rightC); 123 | if (up2) bitClear(joy2,upC); 124 | if (down2) bitClear(joy2,downC); 125 | if (left2) bitClear(joy2,leftC); 126 | if (right2) bitClear(joy2,rightC); 127 | if (fire1) { bitClear(joy1,fire1C); bitClear(joy2,fire1C); } 128 | if (fire2) { bitClear(joy1,fire2C); bitClear(joy2,fire2C); } 129 | 130 | if (GPIOR2 == &GPIOR0) { PORTD &= ~_BV(5); } else { PORTD |= _BV(5); } //TX-LED on, if joystick 3 is activated 131 | if (GPIOR2 == &GPIOR1) { bitClear(joy2,0); } //RX-LED on, if joystick 4 is activated 132 | 133 | GPIOR0 = joy1; GPIOR1 = joy2; 134 | 135 | //PORTB = *ptr; // is this atomic? probably, because ptr is 6-bit pointer. nope... 136 | noInterrupts(); 137 | PORTB = *((unsigned int *)GPIOR2); 138 | //ec8: eb b5 in r30, 0x2b ; 43 139 | //eca: f0 e0 ldi r31, 0x00 ; 0 140 | //ecc: 80 81 ld r24, Z 141 | //ece: 85 b9 out 0x05, r24 ; 5 142 | interrupts(); 143 | 144 | } 145 | 146 | 147 | /* 148 | 149 | Arduino Pro Micro 150 | (led/no pin: PB0, PD5) 151 | 152 | L - PD0 - - 153 | PB1 - PD1 - - 154 | PB2 - i - - 155 | PB3 - i - - 156 | PB4 - PD4 - PF4 157 | PB5 - L - PF5 158 | PB6 PC6 - PE6 PF6 159 | - - PD7 - PF7 160 | 161 | */ 162 | -------------------------------------------------------------------------------- /C64_4joy_adapter/README.md: -------------------------------------------------------------------------------- 1 | - https://www.protovision.games/hardw/4_player.php?language=en 2 | 3 | ``` 4 | & 'C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-objdump.exe' -S "C:\\Users\\lehti\\AppData\\Local\\Temp\\arduino_build_208947/c64_4joystick-adapter.ino.elf" > c:\temp\koe.txt 5 | ``` 6 | 7 | ``` 8 | cli // 1 clock 9 | PORTB = *ptr; // is this atomic? probably, because ptr is 6-bit pointer. nope... 10 | f98: e0 91 26 01 lds r30, 0x0126 ; 0x800126 <__data_end> // 2 clocks 11 | f9c: f0 91 27 01 lds r31, 0x0127 ; 0x800127 <__data_end+0x1> // 2 clocks 12 | fa0: 80 81 ld r24, Z // 1 clock 13 | fa2: 85 b9 out 0x05, r24 ; 5 // 1 clock 14 | sei // 1 clock 15 | ``` 16 | 500ns 17 | 18 | ``` 19 | cli 20 | mov r31, r1 21 | mov r30, gpior0 22 | ld r24, Z 23 | out 0x05, r24 24 | sei 25 | ``` 26 | 375ns 27 | 28 | ## avr 29 | - https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf 30 | - https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf 31 | - http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf 32 | 33 | ## using data/i/o space 34 | - lds can load from io (if +0x20 added to address) or data space 35 | - sts can store from register to io (if +0x20 added to address) or data space 36 | - in can load from io to register 37 | - out can write from register to io 38 | - only one io/data address space access within one instruction? 39 | - lds/sts takes 2 cycles and in/out takes 1 cycle? ldi takes 1 cycle? 40 | - lds/sts: "M" \_SFR_MEM_ADDR(GPIOR2), in/out: "I" \_SFR_IO_ADDR(GPIOR0) 41 | 42 | ## interrupts 43 | - https://billgrundmann.wordpress.com/2009/03/02/the-overhead-of-arduino-interrupts/ 44 | - https://forum.arduino.cc/t/how-fast-can-i-interrupt/25884/5 45 | ``` 46 | This is from the datasheet for the AT90USB82 processor; things in parenthesis are from me... 47 | 48 | -The interrupt execution response for all the enabled AVR interrupts is five clock cycles minimum (the processor is fixin' to execute the interrupt) 49 | -The vector is normally a jump to the interrupt routine, and this jump takes three clock cycles (the processor jumps to the ISR) 50 | -SREG must be saved and restored (the processor doesn't do this for us and SREG is important) 51 | -A return from an interrupt handling routine takes three clock cycles 52 | -When the AVR exits from an interrupt, it will always return to the main program and execute one more instruction before any pending interrupt is served 53 | 54 | Those are the things necessary just to get the ISR called. We have not yet added the application stuff (incrementing an unsigned long in BetterSense's case). 55 | 56 | Adding those up gives us 5+3+2+3+1 = 14. The absolute maximum number of interrupts per second that can be handled by the AT90USB82 is 16 million instructions per second / 14 instructions per interrupt = 1,142,857 interrupts per second. 57 | ``` 58 | 59 | ## avr asm 60 | - http://www.nongnu.org/avr-libc/user-manual/inline_asm.html 61 | - https://ucexperiment.wordpress.com/2016/03/04/arduino-inline-assembly-tutorial-1/ 62 | - https://ucexperiment.wordpress.com/2016/03/11/arduino-inline-assembly-tutorial-5-2/ 63 | 64 | ## atomic 65 | - https://groups.google.com/a/arduino.cc/g/developers/c/cmu0Qy32zxY 66 | - http://www.gammon.com.au/forum/?id=11488 67 | - https://home.csulb.edu/~hill/ee346/Lectures/10%20ATmega32U4%20Interrupts.pdf 68 | 69 | ## register as variable 70 | - https://forum.arduino.cc/index.php?topic=43760.5 71 | - http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_regbind 72 | - volatile register unsigned char my_register asm("r2"); 73 | - https://www.avrfreaks.net/forum/binding-variable-register 74 | 75 | ## running code from ram 76 | - https://forum.arduino.cc/index.php?topic=425962.0 77 | - https://forum.arduino.cc/index.php?topic=470631.0 78 | - "AVRs are Harvard architecture CPUs, so they CANNOT run code out of RAM, so that directive can't possibly do anything of any value whatsoever." 79 | - "Yes, Harvard IS the reason. A Harvard CPU, by definition, has separate code and data memory spaces. They execute code from one memory, and fetch data from a different memory. What you're seeing is most likely the compiler pretending the directive can do what you want, but the linker doing the only thing it can do - putting that code in FLASH." 80 | -------------------------------------------------------------------------------- /C64_4joy_adapter/old/4joy_adapter_old.ino: -------------------------------------------------------------------------------- 1 | // Protovision 4 player interface / Classical Games adapter (1997) 2 | // Arduino Pro Micro 3 | // https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters 4 | // https://www.protovision.games/hardw/build4player.php?language=en 5 | // http://cloud.cbm8bit.com/penfold42/joytester.zip 6 | // https://arduino.stackexchange.com/questions/8758/arduino-interruption-on-pin-change/8926 7 | // http://www.pighixxx.net/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png 8 | // https://opencircuit.shop/ProductInfo/1000378/ATmega32U4-Datasheet.pdf 9 | // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Pro_Micro_v13b.pdf 10 | 11 | // TXD (INT3,PD3) and RXD (INT2,PD2) to userport L. 12 | // INT2(RXD) is used for rising edge and INT3(TXD) for falling edge. 13 | // Interrupts takes only less than 1us to change output port state after select-signal. 14 | 15 | // Joystick port 3 16 | // GND = GND (8) 17 | #define up1 (~PD & _BV(7)) // 6,PD7 = (1) 18 | #define down1 (~PC & _BV(6)) // 5,PC6 = (2) 19 | #define left1 (~PD & _BV(4)) // 4,PD4 = (3) 20 | #define right1 (~PD & _BV(0)) // 3,PD0 = (4) 21 | #define fire1 (~PD & _BV(1)) // 2,PD1 = (6) 22 | 23 | // Joystick port 4 24 | // GND = GND (8) 25 | #define up2 (~PF & _BV(7)) // A0,PF7 = (1) 26 | #define down2 (~PF & _BV(6)) // A1,PF6 = (2) 27 | #define left2 (~PF & _BV(5)) // A2,PF5 = (3) 28 | #define right2 (~PF & _BV(4)) // A3,PF4 = (4) 29 | #define fire2 (~PE & _BV(6)) // 7,PE6 = (6) 30 | 31 | // Arduino <-> Userport 32 | // VCC = +5V (2) 33 | // GND = GND (A) 34 | // TXD+RXD = Select = PB7 (L) 35 | #define upC 1 // 15,PB1 = PB0 (C) 36 | #define downC 3 // 14,PB3 = PB1 (D) 37 | #define leftC 2 // 16,PB2 = PB2 (E) 38 | #define rightC 6 // 10,PB6 = PB3 (F) 39 | #define fire1C 4 // 8,PB4 = PB4 (H) 40 | #define fire2C 5 // 9,PB5 = PB5 (J) 41 | 42 | // LEDS: 43 | // RX = D17,PB0 44 | // TX = -,PD5 45 | 46 | volatile uint8_t output1; 47 | volatile uint8_t output2; 48 | volatile uint16_t last_interrupt; 49 | volatile uint8_t mode = 0; // 0 = 3-joystick mode, 1 = 4-joystick mode 50 | volatile uint8_t last_joystick = 1; 51 | 52 | ISR(INT2_vect, ISR_NAKED) { // rising edge, output joystick 3 53 | asm volatile( 54 | " push r0 \n" // save register r0 55 | " lds r0, output1 \n" 56 | " out %[pin], r0 \n" 57 | " pop r0 \n" // restore previous r0 58 | " rjmp INT2_vect_part_2 \n" // go to part 2 for required prologue and epilogue 59 | :: [pin] "I" (_SFR_IO_ADDR(PORTB))); 60 | } 61 | 62 | ISR(INT2_vect_part_2) { mode = 1; last_joystick = 1; last_interrupt = TCNT1; } 63 | 64 | ISR(INT3_vect, ISR_NAKED) { // falling edge, output joystick 4 65 | asm volatile( 66 | " push r0 \n" // save register r0 67 | " lds r0, output2 \n" 68 | " out %[pin], r0 \n" 69 | " pop r0 \n" // restore previous r0 70 | " rjmp INT3_vect_part_2 \n" // go to part 2 for required prologue and epilogue 71 | :: [pin] "I" (_SFR_IO_ADDR(PORTB))); 72 | } 73 | 74 | ISR(INT3_vect_part_2) { mode = 1; last_joystick = 2; last_interrupt = TCNT1; } 75 | 76 | void setup() { 77 | pinMode(5, INPUT_PULLUP); // pin5 (PC6) is input 78 | pinMode(7, INPUT_PULLUP); // pin7 (PE6) is input 79 | DDRB = 0xff; //all PB-ports are outputs 80 | DDRF = 0; PORTF = 0xff; // all PF-ports are inputs with pullups 81 | DDRD = B00100000; PORTD = B11110011; // all PD-ports are inputs (except PD5) with pullups (PD2,PD3 without pullup) 82 | 83 | TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis()/micros() update ISR) 84 | 85 | EICRA = B10110000; // INT2 – rising edge on RXD (Bxx11xxxx), INT3 - falling edge on TXD (B10xxxxxx) 86 | EIMSK = B1100; // enable INT2 (Bx1xx) and INT3 (B1xxx) 87 | 88 | //Serial.begin(115200); 89 | //PORTD &= ~_BV(5); 90 | 91 | // We can't use millis() or micros() because Timer0 interrupts are disabled. We use 16-bit Timer1 with 1024 prescaler as "clock". 92 | TIMSK1 = 0; // disable timer1 interrupts 93 | TCCR1A = 0; 94 | TCCR1B = B00000101; // Timer1, normal mode, prescaler 1024. One tick is 64us. 95 | TCNT1 = 0; // reset Timer1 counter 96 | } 97 | 98 | volatile uint8_t joy1, joy2; 99 | 100 | void loop() { 101 | uint8_t PF, PD, PC, PE; 102 | PF = PINF; PD = PIND; PC = PINC; PE = PINE; 103 | joy1 = 0xff; joy2 = 0xff; // all signals are inverted (also LED) 104 | if (up1) bitClear(joy1,upC); 105 | if (down1) bitClear(joy1,downC); 106 | if (left1) bitClear(joy1,leftC); 107 | if (right1) bitClear(joy1,rightC); 108 | if (up2) bitClear(joy2,upC); 109 | if (down2) bitClear(joy2,downC); 110 | if (left2) bitClear(joy2,leftC); 111 | if (right2) bitClear(joy2,rightC); 112 | if (fire1) { bitClear(joy1,fire1C); bitClear(joy2,fire1C); } 113 | if (fire2) { bitClear(joy1,fire2C); bitClear(joy2,fire2C); } 114 | //mode = 1; 115 | //if (mode) { bitClear(joy1,0); bitClear(joy2,0); } // RX LED is in PB0 116 | if (mode) { PORTD &= ~_BV(5); } else { PORTD |= _BV(5); } // TX LED is in PD5 117 | //bitClear(joy1,0); bitClear(joy2,0); 118 | //joy1 = 0; joy2 = 0; 119 | //PORTD |= _BV(5); 120 | output1 = joy1; output2 = joy2; 121 | 122 | if (mode == 1) { 123 | uint16_t now = TCNT1; // TCNT1 is special 16-bit register, so it must be copied to variable before it can be used 124 | if ((now - last_interrupt) > 15625) mode = 0; // if there is no select-signal in 1s, fallback to 3-joystick mode 125 | } else { 126 | if (last_joystick == 1) PORTB = output1; // 3-joystick mode, update repeatedly and only joystick 3 127 | if (last_joystick == 2) PORTB = output2; // 3-joystick mode, update repeatedly and only joystick 4 128 | } 129 | //PORTB = output1; // 3-joystick mode, update repeatedly and only joystick 3 130 | //Serial.print(joy1, BIN); Serial.print(" "); Serial.println(joy2, BIN); delay(100); 131 | //Serial.print(mode, BIN); Serial.print(" "); Serial.println(last_interrupt); 132 | //delayMicroseconds(50); 133 | delayMicroseconds(10); 134 | } 135 | -------------------------------------------------------------------------------- /C64_4joy_adapter/old/4joy_adapter_old2.ino: -------------------------------------------------------------------------------- 1 | // Protovision 4 player interface / Classical Games adapter (1997) 2 | // Arduino Pro Micro 3 | // https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters 4 | // https://www.protovision.games/hardw/build4player.php?language=en 5 | // http://cloud.cbm8bit.com/penfold42/joytester.zip 6 | // https://arduino.stackexchange.com/questions/8758/arduino-interruption-on-pin-change/8926 7 | // http://www.nongnu.org/avr-libc/user-manual/inline_asm.html 8 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf 9 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf 10 | // http://www.pighixxx.net/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png 11 | // https://opencircuit.shop/ProductInfo/1000378/ATmega32U4-Datasheet.pdf 12 | // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Pro_Micro_v13b.pdf 13 | 14 | // TXD (INT3,PD3) and RXD (INT2,PD2) to userport L. 15 | // INT2(RXD) is used for rising edge and INT3(TXD) for falling edge. 16 | // Interrupts takes only less than 1us to change output port state after select-signal. 17 | 18 | // Joystick port 3 19 | // GND = GND (8) 20 | #define up1 (~PD & _BV(7)) // 6,PD7 = (1) 21 | #define down1 (~PC & _BV(6)) // 5,PC6 = (2) 22 | #define left1 (~PD & _BV(4)) // 4,PD4 = (3) 23 | #define right1 (~PD & _BV(0)) // 3,PD0 = (4) 24 | #define fire1 (~PD & _BV(1)) // 2,PD1 = (6) 25 | 26 | // Joystick port 4 27 | // GND = GND (8) 28 | #define up2 (~PF & _BV(7)) // A0,PF7 = (1) 29 | #define down2 (~PF & _BV(6)) // A1,PF6 = (2) 30 | #define left2 (~PF & _BV(5)) // A2,PF5 = (3) 31 | #define right2 (~PF & _BV(4)) // A3,PF4 = (4) 32 | #define fire2 (~PE & _BV(6)) // 7,PE6 = (6) 33 | 34 | // Arduino <-> Userport 35 | // VCC = +5V (2) 36 | // GND = GND (A) 37 | #define upC 1 // 15,PB1 = PB0 (C) 38 | #define downC 3 // 14,PB3 = PB1 (D) 39 | #define leftC 2 // 16,PB2 = PB2 (E) 40 | #define rightC 6 // 10,PB6 = PB3 (F) 41 | #define fire1C 4 // 8,PB4 = PB4 (H) 42 | #define fire2C 5 // 9,PB5 = PB5 (J) 43 | #define selectC (PIND & _BV(2)) // RXD,PD2(INT2)+TXD,PD3(INT3) = PB7 (L) 44 | 45 | // LEDS (inverted): 46 | // RX = D17,PB0 47 | // TX = -,PD5 48 | 49 | //volatile uint16_t last_interrupt; 50 | volatile uint8_t *ptr; 51 | 52 | ISR(INT2_vect, ISR_NAKED) { // rising edge, output joystick 3 53 | asm volatile( 54 | //" push r0 \n" // save register r0 55 | //" lds r0, output1 \n" 56 | " out %[pin], %[gpio] \n" // GPIOR0 address is 30, so we can use it directly with out-command (which works with 0-31) 57 | //" pop r0 \n" // restore previous r0 58 | " rjmp INT2_vect_part_2 \n" // go to part 2 for required prologue and epilogue 59 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR0))); 60 | } 61 | 62 | ISR(INT2_vect_part_2) { ptr = &GPIOR0; } 63 | 64 | ISR(INT3_vect, ISR_NAKED) { // falling edge, output joystick 4 65 | asm volatile( 66 | " push r0 \n" // save register r0 67 | " lds r0, %[gpio] \n" // GPIOR1 address is 42 and out-command works only with 0-31 68 | " out %[pin], r0 \n" // so we need lds-command and r0 register 69 | " pop r0 \n" // restore previous r0 70 | " rjmp INT3_vect_part_2 \n" // go to part 2 for required prologue and epilogue 71 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR1))); 72 | } 73 | 74 | ISR(INT3_vect_part_2) { ptr = &GPIOR1; } 75 | 76 | void setup() { 77 | DDRB = 0xff; PORTB = 0xff; //all PB-ports are outputs and high (0xff = zero state, because signals are inverted) 78 | DDRF = 0; PORTF = 0xff; // all PF-ports are inputs with pullups 79 | DDRD = B00100000; PORTD = B11110011; // all PD-ports are inputs (except PD5) with pullups (PD2,PD3 without pullup) 80 | pinMode(5, INPUT_PULLUP); // pin5 (PC6) is input 81 | pinMode(7, INPUT_PULLUP); // pin7 (PE6) is input 82 | 83 | if (selectC) ptr = &GPIOR0; else ptr = &GPIOR1; 84 | GPIOR0 = 0xff; GPIOR1 = 0xff; // start from zero state (signals are inverted) 85 | 86 | TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis()/micros() update ISR) 87 | 88 | EICRA = B10110000; // INT2 – rising edge on RXD (Bxx11xxxx), INT3 - falling edge on TXD (B10xxxxxx) 89 | EIMSK = B1100; // enable INT2 (Bx1xx) and INT3 (B1xxx) 90 | 91 | //Serial.begin(115200); 92 | //PORTD &= ~_BV(5); // TX-LED on 93 | 94 | // We can't use millis() or micros() because Timer0 interrupts are disabled. We use 16-bit Timer1 with 1024 prescaler as "clock". 95 | /*TIMSK1 = 0; // disable timer1 interrupts 96 | TCCR1A = 0; 97 | TCCR1B = B00000101; // Timer1, normal mode, prescaler 1024. One tick is 64us. 98 | TCNT1 = 0; // reset Timer1 counter*/ 99 | } 100 | 101 | void loop() { 102 | uint8_t PF, PD, PC, PE; 103 | PF = PINF; PD = PIND; PC = PINC; PE = PINE; 104 | uint8_t joy1 = 0xff; uint8_t joy2 = 0xff; // all signals are inverted 105 | if (up1) bitClear(joy1,upC); 106 | if (down1) bitClear(joy1,downC); 107 | if (left1) bitClear(joy1,leftC); 108 | if (right1) bitClear(joy1,rightC); 109 | if (up2) bitClear(joy2,upC); 110 | if (down2) bitClear(joy2,downC); 111 | if (left2) bitClear(joy2,leftC); 112 | if (right2) bitClear(joy2,rightC); 113 | if (fire1) { bitClear(joy1,fire1C); bitClear(joy2,fire1C); } 114 | if (fire2) { bitClear(joy1,fire2C); bitClear(joy2,fire2C); } 115 | 116 | if (ptr == &GPIOR0) { PORTD &= ~_BV(5); } else { PORTD |= _BV(5); } //TX-LED on, if joystick 3 are activated 117 | if (ptr == &GPIOR1) { bitClear(joy2,0); } //RX-LED on, if joystick 4 are activated 118 | 119 | GPIOR0 = joy1; GPIOR1 = joy2; 120 | 121 | PORTB = *ptr; // is this atomic? probably, because ptr is 6-bit pointer. 122 | //delayMicroseconds(10); 123 | //uint16_t koe = ptr; 124 | //Serial.println(koe, HEX); 125 | //delayMicroseconds(10000); 126 | } 127 | -------------------------------------------------------------------------------- /C64_4joy_adapter/old/4joy_adapter_old3.ino: -------------------------------------------------------------------------------- 1 | // Protovision 4 player interface / Classical Games adapter (1997) 2 | // Arduino Pro Micro 3 | // https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters 4 | // https://www.protovision.games/hardw/build4player.php?language=en 5 | // http://cloud.cbm8bit.com/penfold42/joytester.zip 6 | // https://arduino.stackexchange.com/questions/8758/arduino-interruption-on-pin-change/8926 7 | // http://www.nongnu.org/avr-libc/user-manual/inline_asm.html 8 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf 9 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf 10 | // http://www.pighixxx.net/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png 11 | // https://opencircuit.shop/ProductInfo/1000378/ATmega32U4-Datasheet.pdf 12 | // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Pro_Micro_v13b.pdf 13 | 14 | // TXD (INT3,PD3) and RXD (INT2,PD2) to userport L. 15 | // INT2(RXD) is used for rising edge and INT3(TXD) for falling edge. 16 | // Interrupts takes only less than 1us to change output port state after select-signal. 17 | 18 | // Joystick port 3 19 | // GND = GND (8) 20 | #define up1 (~PD & _BV(7)) // 6,PD7 = (1) 21 | #define down1 (~PC & _BV(6)) // 5,PC6 = (2) 22 | #define left1 (~PD & _BV(4)) // 4,PD4 = (3) 23 | #define right1 (~PD & _BV(0)) // 3,PD0 = (4) 24 | #define fire1 (~PD & _BV(1)) // 2,PD1 = (6) 25 | 26 | // Joystick port 4 27 | // GND = GND (8) 28 | #define up2 (~PF & _BV(7)) // A0,PF7 = (1) 29 | #define down2 (~PF & _BV(6)) // A1,PF6 = (2) 30 | #define left2 (~PF & _BV(5)) // A2,PF5 = (3) 31 | #define right2 (~PF & _BV(4)) // A3,PF4 = (4) 32 | #define fire2 (~PE & _BV(6)) // 7,PE6 = (6) 33 | 34 | // Arduino <-> Userport 35 | // VCC = +5V (2) 36 | // GND = GND (A) 37 | #define upC 1 // 15,PB1 = PB0 (C) 38 | #define downC 3 // 14,PB3 = PB1 (D) 39 | #define leftC 2 // 16,PB2 = PB2 (E) 40 | #define rightC 6 // 10,PB6 = PB3 (F) 41 | #define fire1C 4 // 8,PB4 = PB4 (H) 42 | #define fire2C 5 // 9,PB5 = PB5 (J) 43 | #define selectC (PIND & _BV(2)) // RXD,PD2(INT2)+TXD,PD3(INT3) = PB7 (L) 44 | 45 | // LEDS (inverted): 46 | // RX = D17,PB0 47 | // TX = -,PD5 48 | 49 | //volatile uint16_t last_interrupt; 50 | //volatile uint8_t *ptr; 51 | 52 | ISR(INT2_vect, ISR_NAKED) { // rising edge, output joystick 3 53 | asm volatile( 54 | " push r0 \n" 55 | " in r0, %[gpio] \n" 56 | " out %[pin], r0 \n" 57 | " pop r0 \n" 58 | " rjmp INT2_vect_part_2 \n" // go to part 2 for required prologue and epilogue 59 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR0))); 60 | } 61 | 62 | ISR(INT2_vect_part_2) { GPIOR2 = &GPIOR0; } 63 | 64 | ISR(INT3_vect, ISR_NAKED) { // falling edge, output joystick 4 65 | asm volatile( 66 | " push r0 \n" 67 | // " lds r0, %[gpio] \n" // GPIOR1 address is 42 68 | " in r0, %[gpio] \n" 69 | " out %[pin], r0 \n" 70 | " pop r0 \n" 71 | " rjmp INT3_vect_part_2 \n" // go to part 2 for required prologue and epilogue 72 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR1))); 73 | //:: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "M" (_SFR_MEM_ADDR(GPIOR1))); 74 | } 75 | 76 | ISR(INT3_vect_part_2) { GPIOR2 = &GPIOR1; } 77 | 78 | void setup() { 79 | DDRB = 0xff; PORTB = 0xff; //all PB-ports are outputs and high (0xff = zero state, because signals are inverted) 80 | DDRF = 0; PORTF = 0xff; // all PF-ports are inputs with pullups 81 | DDRD = B00100000; PORTD = B11110011; // all PD-ports are inputs (except PD5) with pullups (PD2,PD3 without pullup) 82 | pinMode(5, INPUT_PULLUP); // pin5 (PC6) is input 83 | pinMode(7, INPUT_PULLUP); // pin7 (PE6) is input 84 | 85 | if (selectC) GPIOR2 = &GPIOR0; else GPIOR2 = &GPIOR1; 86 | GPIOR0 = 0xff; GPIOR1 = 0xff; // start from zero state (signals are inverted) 87 | 88 | TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis()/micros() update ISR) 89 | 90 | EICRA = B10110000; // INT2 – rising edge on RXD (Bxx11xxxx), INT3 - falling edge on TXD (B10xxxxxx) 91 | EIMSK = B1100; // enable INT2 (Bx1xx) and INT3 (B1xxx) 92 | 93 | //Serial.begin(115200); 94 | //PORTD &= ~_BV(5); // TX-LED on 95 | 96 | // We can't use millis() or micros() because Timer0 interrupts are disabled. We use 16-bit Timer1 with 1024 prescaler as "clock". 97 | /*TIMSK1 = 0; // disable timer1 interrupts 98 | TCCR1A = 0; 99 | TCCR1B = B00000101; // Timer1, normal mode, prescaler 1024. One tick is 64us. 100 | TCNT1 = 0; // reset Timer1 counter*/ 101 | } 102 | 103 | void loop() { 104 | uint8_t PF, PD, PC, PE; 105 | PF = PINF; PD = PIND; PC = PINC; PE = PINE; 106 | uint8_t joy1 = 0xff; uint8_t joy2 = 0xff; // all signals are inverted 107 | if (up1) bitClear(joy1,upC); 108 | if (down1) bitClear(joy1,downC); 109 | if (left1) bitClear(joy1,leftC); 110 | if (right1) bitClear(joy1,rightC); 111 | if (up2) bitClear(joy2,upC); 112 | if (down2) bitClear(joy2,downC); 113 | if (left2) bitClear(joy2,leftC); 114 | if (right2) bitClear(joy2,rightC); 115 | if (fire1) { bitClear(joy1,fire1C); bitClear(joy2,fire1C); } 116 | if (fire2) { bitClear(joy1,fire2C); bitClear(joy2,fire2C); } 117 | 118 | if (GPIOR2 == &GPIOR0) { PORTD &= ~_BV(5); } else { PORTD |= _BV(5); } //TX-LED on, if joystick 3 is activated 119 | if (GPIOR2 == &GPIOR1) { bitClear(joy2,0); } //RX-LED on, if joystick 4 is activated 120 | 121 | GPIOR0 = joy1; GPIOR1 = joy2; 122 | 123 | //PORTB = *ptr; // is this atomic? probably, because ptr is 6-bit pointer. nope... 124 | noInterrupts(); 125 | PORTB = *((unsigned int *)GPIOR2); 126 | //ec8: eb b5 in r30, 0x2b ; 43 127 | //eca: f0 e0 ldi r31, 0x00 ; 0 128 | //ecc: 80 81 ld r24, Z 129 | //ece: 85 b9 out 0x05, r24 ; 5 130 | interrupts(); 131 | 132 | /*asm volatile( 133 | " clr r31 \n" //Z is r31:r30. Z is pointer. 134 | " cli \n" 135 | " lds r30, %[gpio] \n" 136 | " ld __tmp_reg__, Z \n" 137 | " sts %[pin], __tmp_reg__ \n" 138 | " sei \n" 139 | :: [pin] "M" (_SFR_MEM_ADDR(PORTB)), [gpio] "M" (_SFR_MEM_ADDR(GPIOR2)) : "r30", "r31");*/ 140 | //ed2: ff 27 eor r31, r31 141 | //ed4: f8 94 cli 142 | //ed6: e0 91 4b 00 lds r30, 0x004B ; 0x80004b <__TEXT_REGION_LENGTH__+0x7e004b> 143 | //eda: 00 80 ld r0, Z 144 | //edc: 00 92 25 00 sts 0x0025, r0 ; 0x800025 <__TEXT_REGION_LENGTH__+0x7e0025> 145 | //ee0: 78 94 sei 146 | 147 | //delayMicroseconds(10); 148 | //uint16_t koe = ptr; 149 | //Serial.println(koe, HEX); 150 | //delayMicroseconds(10000); 151 | } 152 | -------------------------------------------------------------------------------- /C64_4joy_adapter/old/interrupt_test.ino: -------------------------------------------------------------------------------- 1 | #define LED_PIN 5 // digital pin #13 (portb) (pin #9 with Arduino Pro Micro) 2 | #define LED_ON() PORTB |= _BV(LED_PIN) 3 | #define LED_OFF() PORTB &= ~_BV(LED_PIN) 4 | 5 | volatile uint8_t output1; 6 | volatile uint8_t output2; 7 | 8 | ISR(INT0_vect, ISR_NAKED) 9 | { 10 | asm volatile( 11 | " push r0 \n" // save register r0 12 | " lds r0, output1 \n" 13 | " out %[pin], r0 \n" 14 | " pop r0 \n" // restore previous r0 15 | " rjmp INT0_vect_part_2 \n" // go to part 2 for required prologue and epilogue 16 | :: [pin] "I" (_SFR_IO_ADDR(PORTB))); 17 | } 18 | 19 | ISR(INT0_vect_part_2) { 20 | } 21 | 22 | ISR(INT1_vect, ISR_NAKED) 23 | { 24 | asm volatile( 25 | " push r0 \n" // save register r0 26 | " lds r0, output2 \n" 27 | " out %[pin], r0 \n" 28 | " pop r0 \n" // restore previous r0 29 | " rjmp INT1_vect_part_2 \n" // go to part 2 for required prologue and epilogue 30 | :: [pin] "I" (_SFR_IO_ADDR(PORTB))); 31 | } 32 | 33 | ISR(INT1_vect_part_2) { 34 | } 35 | 36 | void setup() { 37 | //pinMode(13, OUTPUT); // Arduino Uno 38 | pinMode(9, OUTPUT); // Arduino Pro Micro 39 | TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis() update ISR) 40 | EICRA = B1011; // INT0 – rising edge on 2 (B11), INT1 - falling edge on 3 (B10xx) 41 | EIMSK = B11; // enable int0 (Bx1) and int1 (B1x) 42 | } 43 | 44 | void loop() { 45 | LED_ON(); 46 | //LED_OFF(); 47 | output1 = 0; 48 | delayMicroseconds(50); 49 | } 50 | -------------------------------------------------------------------------------- /C64_4joy_adapter/old/stuff.ino: -------------------------------------------------------------------------------- 1 | // Protovision 4 player interface / Classical Games adapter (1997) 2 | // Arduino Pro Micro 3 | // https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters 4 | // https://www.protovision.games/hardw/build4player.php?language=en 5 | // http://cloud.cbm8bit.com/penfold42/joytester.zip 6 | // https://arduino.stackexchange.com/questions/8758/arduino-interruption-on-pin-change/8926 7 | // http://www.nongnu.org/avr-libc/user-manual/inline_asm.html 8 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf 9 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf 10 | // http://www.pighixxx.net/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png 11 | // https://opencircuit.shop/ProductInfo/1000378/ATmega32U4-Datasheet.pdf 12 | // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Pro_Micro_v13b.pdf 13 | 14 | // TXD (INT3,PD3) and RXD (INT2,PD2) to userport L. 15 | // INT2(RXD) is used for rising edge and INT3(TXD) for falling edge. 16 | // Interrupts takes only less than 1us to change output port state after select-signal. 17 | 18 | // Joystick port 3 19 | // GND = GND (8) 20 | #define up1 (~PD & _BV(7)) // 6,PD7 = (1) 21 | #define down1 (~PC & _BV(6)) // 5,PC6 = (2) 22 | #define left1 (~PD & _BV(4)) // 4,PD4 = (3) 23 | #define right1 (~PD & _BV(0)) // 3,PD0 = (4) 24 | #define fire1 (~PD & _BV(1)) // 2,PD1 = (6) 25 | 26 | // Joystick port 4 27 | // GND = GND (8) 28 | #define up2 (~PF & _BV(7)) // A0,PF7 = (1) 29 | #define down2 (~PF & _BV(6)) // A1,PF6 = (2) 30 | #define left2 (~PF & _BV(5)) // A2,PF5 = (3) 31 | #define right2 (~PF & _BV(4)) // A3,PF4 = (4) 32 | #define fire2 (~PE & _BV(6)) // 7,PE6 = (6) 33 | 34 | // Arduino <-> Userport 35 | // VCC = +5V (2) 36 | // GND = GND (A) 37 | #define upC 1 // 15,PB1 = PB0 (C) 38 | #define downC 3 // 14,PB3 = PB1 (D) 39 | #define leftC 2 // 16,PB2 = PB2 (E) 40 | #define rightC 6 // 10,PB6 = PB3 (F) 41 | #define fire1C 4 // 8,PB4 = PB4 (H) 42 | #define fire2C 5 // 9,PB5 = PB5 (J) 43 | #define selectC (PIND & _BV(2)) // RXD,PD2(INT2)+TXD,PD3(INT3) = PB7 (L) 44 | 45 | // LEDS (inverted): 46 | // RX = D17,PB0 47 | // TX = -,PD5 48 | 49 | //volatile uint16_t last_interrupt; 50 | volatile uint8_t *ptr; 51 | 52 | ISR(INT2_vect, ISR_NAKED) { // rising edge, output joystick 3 53 | asm volatile( 54 | " push r0 \n" // save register r0 55 | " lds r0, %[gpio] \n" // GPIOR1 address is 42 and out-command works only with 0-31 56 | " out %[pin], r0 \n" // so we need lds-command and r0 register 57 | " pop r0 \n" // restore previous r0 58 | " out %[gpio0], %[gpio] \n" // restore previous r0 59 | " rjmp INT2_vect_part_2 \n" // go to part 2 for required prologue and epilogue 60 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio0] "I" (_SFR_IO_ADDR(GPIOR0)), [gpio] "I" (_SFR_IO_ADDR(GPIOR1))); 61 | } 62 | 63 | ISR(INT2_vect_part_2) { GPIOR0 = &GPIOR1; } 64 | 65 | ISR(INT3_vect, ISR_NAKED) { // falling edge, output joystick 4 66 | asm volatile( 67 | " push r0 \n" // save register r0 68 | " lds r0, %[gpio] \n" // GPIOR2 address is 42 and out-command works only with 0-31 69 | " out %[pin], r0 \n" // so we need lds-command and r0 register 70 | " pop r0 \n" // restore previous r0 71 | " rjmp INT3_vect_part_2 \n" // go to part 2 for required prologue and epilogue 72 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR2))); 73 | } 74 | 75 | ISR(INT3_vect_part_2) { GPIOR0 = &GPIOR2; } 76 | 77 | void setup() { 78 | DDRB = 0xff; PORTB = 0xff; //all PB-ports are outputs and high (0xff = zero state, because signals are inverted) 79 | DDRF = 0; PORTF = 0xff; // all PF-ports are inputs with pullups 80 | DDRD = B00100000; PORTD = B11110011; // all PD-ports are inputs (except PD5) with pullups (PD2,PD3 without pullup) 81 | pinMode(5, INPUT_PULLUP); // pin5 (PC6) is input 82 | pinMode(7, INPUT_PULLUP); // pin7 (PE6) is input 83 | 84 | if (selectC) ptr = &GPIOR0; else ptr = &GPIOR1; 85 | GPIOR0 = 0xff; GPIOR1 = 0xff; // start from zero state (signals are inverted) 86 | 87 | TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis()/micros() update ISR) 88 | 89 | EICRA = B10110000; // INT2 – rising edge on RXD (Bxx11xxxx), INT3 - falling edge on TXD (B10xxxxxx) 90 | EIMSK = B1100; // enable INT2 (Bx1xx) and INT3 (B1xxx) 91 | 92 | //Serial.begin(115200); 93 | //PORTD &= ~_BV(5); // TX-LED on 94 | 95 | // We can't use millis() or micros() because Timer0 interrupts are disabled. We use 16-bit Timer1 with 1024 prescaler as "clock". 96 | /*TIMSK1 = 0; // disable timer1 interrupts 97 | TCCR1A = 0; 98 | TCCR1B = B00000101; // Timer1, normal mode, prescaler 1024. One tick is 64us. 99 | TCNT1 = 0; // reset Timer1 counter*/ 100 | } 101 | 102 | void loop() { 103 | uint8_t PF, PD, PC, PE; 104 | PF = PINF; PD = PIND; PC = PINC; PE = PINE; 105 | uint8_t joy1 = 0xff; uint8_t joy2 = 0xff; // all signals are inverted 106 | if (up1) bitClear(joy1,upC); 107 | if (down1) bitClear(joy1,downC); 108 | if (left1) bitClear(joy1,leftC); 109 | if (right1) bitClear(joy1,rightC); 110 | if (up2) bitClear(joy2,upC); 111 | if (down2) bitClear(joy2,downC); 112 | if (left2) bitClear(joy2,leftC); 113 | if (right2) bitClear(joy2,rightC); 114 | if (fire1) { bitClear(joy1,fire1C); bitClear(joy2,fire1C); } 115 | if (fire2) { bitClear(joy1,fire2C); bitClear(joy2,fire2C); } 116 | 117 | if (GPIOR0 == &GPIOR1) { PORTD &= ~_BV(5); } else { PORTD |= _BV(5); } //TX-LED on, if joystick 3 are activated 118 | if (GPIOR0 == &GPIOR2) { bitClear(joy2,0); } //RX-LED on, if joystick 4 are activated 119 | 120 | GPIOR1 = joy1; GPIOR2 = joy2; 121 | 122 | //noInterrupts(); 123 | //PORTB = *ptr; // is this atomic? probably, because ptr is 6-bit pointer. 124 | //interrupts(); 125 | 126 | asm volatile( 127 | " cli \n" 128 | " clr r31 \n" 129 | " mov r30, %[gpio] \n" 130 | " ld r24, Z \n" 131 | " out %[pin], r24 \n" 132 | " sei \n" 133 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR0))); 134 | 135 | //delayMicroseconds(10); 136 | //uint16_t koe = ptr; 137 | //Serial.println(koe, HEX); 138 | //delayMicroseconds(10000); 139 | } 140 | -------------------------------------------------------------------------------- /C64_4joy_adapter/old/temp.ino: -------------------------------------------------------------------------------- 1 | // Protovision 4 player interface / Classical Games adapter (1997) 2 | // Arduino Pro Micro 3 | // https://en.wikipedia.org/wiki/Commodore_64_joystick_adapters 4 | // https://www.protovision.games/hardw/build4player.php?language=en 5 | // http://cloud.cbm8bit.com/penfold42/joytester.zip 6 | // https://arduino.stackexchange.com/questions/8758/arduino-interruption-on-pin-change/8926 7 | // http://www.nongnu.org/avr-libc/user-manual/inline_asm.html 8 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-7766-8-bit-avr-atmega16u4-32u4_datasheet.pdf 9 | // http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf 10 | // http://www.pighixxx.net/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png 11 | // https://opencircuit.shop/ProductInfo/1000378/ATmega32U4-Datasheet.pdf 12 | // https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/Pro_Micro_v13b.pdf 13 | 14 | // TXD (INT3,PD3) and RXD (INT2,PD2) to userport L. 15 | // INT2(RXD) is used for rising edge and INT3(TXD) for falling edge. 16 | // Interrupts takes only less than 1us to change output port state after select-signal. 17 | 18 | // Joystick port 3 19 | // GND = GND (8) 20 | #define up1 (~PD & _BV(7)) // 6,PD7 = (1) 21 | #define down1 (~PC & _BV(6)) // 5,PC6 = (2) 22 | #define left1 (~PD & _BV(4)) // 4,PD4 = (3) 23 | #define right1 (~PD & _BV(0)) // 3,PD0 = (4) 24 | #define fire1 (~PD & _BV(1)) // 2,PD1 = (6) 25 | 26 | // Joystick port 4 27 | // GND = GND (8) 28 | #define up2 (~PF & _BV(7)) // A0,PF7 = (1) 29 | #define down2 (~PF & _BV(6)) // A1,PF6 = (2) 30 | #define left2 (~PF & _BV(5)) // A2,PF5 = (3) 31 | #define right2 (~PF & _BV(4)) // A3,PF4 = (4) 32 | #define fire2 (~PE & _BV(6)) // 7,PE6 = (6) 33 | 34 | // Arduino <-> Userport 35 | // VCC = +5V (2) 36 | // GND = GND (A) 37 | // TXD+RXD = Select = PB7 (L) 38 | #define upC 1 // 15,PB1 = PB0 (C) 39 | #define downC 3 // 14,PB3 = PB1 (D) 40 | #define leftC 2 // 16,PB2 = PB2 (E) 41 | #define rightC 6 // 10,PB6 = PB3 (F) 42 | #define fire1C 4 // 8,PB4 = PB4 (H) 43 | #define fire2C 5 // 9,PB5 = PB5 (J) 44 | 45 | // LEDS (inverted): 46 | // RX = D17,PB0 47 | // TX = -,PD5 48 | 49 | //volatile uint16_t last_interrupt; 50 | volatile uint8_t *ptr; 51 | 52 | ISR(INT2_vect, ISR_NAKED) { // rising edge, output joystick 3 53 | asm volatile( 54 | //" push r0 \n" // save register r0 55 | //" lds r0, output1 \n" 56 | " out %[pin], %[gpio] \n" // GPIOR0 address is 30, so we can use it directly with out-command (which works with 0-31) 57 | //" pop r0 \n" // restore previous r0 58 | " rjmp INT2_vect_part_2 \n" // go to part 2 for required prologue and epilogue 59 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR0))); 60 | } 61 | 62 | //ISR(INT2_vect_part_2) { ptr = &GPIOR0; last_interrupt = TCNT1; } 63 | ISR(INT2_vect_part_2) { ptr = &GPIOR0; } 64 | 65 | ISR(INT3_vect, ISR_NAKED) { // falling edge, output joystick 4 66 | asm volatile( 67 | " push r0 \n" // save register r0 68 | " lds r0, %[gpio] \n" // GPIOR1 address is 42 and out-command works only with 0-31 69 | " out %[pin], r0 \n" // so we need lds-command and r0 register 70 | " pop r0 \n" // restore previous r0 71 | " rjmp INT3_vect_part_2 \n" // go to part 2 for required prologue and epilogue 72 | :: [pin] "I" (_SFR_IO_ADDR(PORTB)), [gpio] "I" (_SFR_IO_ADDR(GPIOR1))); 73 | } 74 | 75 | //ISR(INT3_vect_part_2) { ptr = &GPIOR1; last_interrupt = TCNT1; } 76 | ISR(INT3_vect_part_2) { ptr = &GPIOR1; } 77 | 78 | void setup() { 79 | ptr = &GPIOR0; 80 | 81 | pinMode(5, INPUT_PULLUP); // pin5 (PC6) is input 82 | pinMode(7, INPUT_PULLUP); // pin7 (PE6) is input 83 | DDRB = 0xff; //all PB-ports are outputs 84 | DDRF = 0; PORTF = 0xff; // all PF-ports are inputs with pullups 85 | DDRD = B00100000; PORTD = B11010011; // all PD-ports are inputs (except PD5) with pullups (PD2,PD3 without pullup), TX-LED on 86 | 87 | TIMSK0 = 0; // disable timer0 interrupts (Arduino Uno/Pro Micro millis()/micros() update ISR) 88 | 89 | EICRA = B10110000; // INT2 – rising edge on RXD (Bxx11xxxx), INT3 - falling edge on TXD (B10xxxxxx) 90 | EIMSK = B1100; // enable INT2 (Bx1xx) and INT3 (B1xxx) 91 | 92 | //Serial.begin(115200); 93 | //PORTD &= ~_BV(5); // TX-LED on 94 | 95 | // We can't use millis() or micros() because Timer0 interrupts are disabled. We use 16-bit Timer1 with 1024 prescaler as "clock". 96 | /*TIMSK1 = 0; // disable timer1 interrupts 97 | TCCR1A = 0; 98 | TCCR1B = B00000101; // Timer1, normal mode, prescaler 1024. One tick is 64us. 99 | TCNT1 = 0; // reset Timer1 counter*/ 100 | } 101 | 102 | volatile uint8_t joy1, joy2; 103 | 104 | void loop() { 105 | uint8_t PF, PD, PC, PE; 106 | PF = PINF; PD = PIND; PC = PINC; PE = PINE; 107 | joy1 = 0xff; joy2 = 0xff; // all signals are inverted 108 | if (up1) bitClear(joy1,upC); 109 | if (down1) bitClear(joy1,downC); 110 | if (left1) bitClear(joy1,leftC); 111 | if (right1) bitClear(joy1,rightC); 112 | if (up2) bitClear(joy2,upC); 113 | if (down2) bitClear(joy2,downC); 114 | if (left2) bitClear(joy2,leftC); 115 | if (right2) bitClear(joy2,rightC); 116 | if (fire1) { bitClear(joy1,fire1C); bitClear(joy2,fire1C); } 117 | if (fire2) { bitClear(joy1,fire2C); bitClear(joy2,fire2C); } 118 | 119 | GPIOR0 = joy1; GPIOR1 = joy2; 120 | 121 | PORTB = *ptr; // is this atomic? probably, because ptr is 6-bit pointer? 122 | //delayMicroseconds(10); 123 | } 124 | -------------------------------------------------------------------------------- /C64_joystick_atmelstudio/README.md: -------------------------------------------------------------------------------- 1 | ## Very simple 2 button single Atari-joystick adapter for Arduino Pro Micro (ATmega32U4). Made with Atmel Studio 7 and Lufa. 2 | 3 | ## Building 4 | - 9-pin D-connector -> Arduino Pro Micro 5 | - UP(1) -> 10(PB6) 6 | - DOWN(2) -> 16(PB2) 7 | - LEFT(3) -> 14(PB3) 8 | - RIGHT(4) -> 15(PB1) 9 | - BTN1(6) -> 9(PB5) 10 | - BTN2(C64/SMS:9, MSX:7) -> 8(PB4) (optional) 11 | - GND(8) -> GND 12 | 13 | ## Firmware/flashing 14 | - Install Arduino IDE for avrdude: https://www.arduino.cc/en/main/software 15 | - Download firmware: https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/master/C64_joystick_atmelstudio/CLASS_JOYSTICK1.hex 16 | - Connect RST to GND couple of times to get Arduino Pro Micro to programming mode (notice that com-port is different in programming mode in Windows) 17 | - Flash firmware: 18 | ``` 19 | & "C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avrdude" -C"C:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf" -v -p m32u4 -c avr109 -P COM5 -b 57600 -U flash:w:CLASS_JOYSTICK1.hex:i 20 | ``` 21 | 22 | ## Muistiinpanoja 23 | ``` 24 | 13.2.2019: 25 | Tools->Extensions and Updates...->Lufa 26 | Lufa->New Example Project->FourWalledCubicle - Dean Camera->Joystick HID Device Demo (Class Driver APIs) - AVR8 Architecture 27 | Project->Properties->Change Device...->ATmega32U4 (rutkuttaa jotain, mutta suostuu silti) 28 | 29 | Tools->External tools 30 | Title: 31 | Arduino_via_bootloader 32 | command: 33 | C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avrdude.exe 34 | Arguments: 35 | -C"C:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf" -v -patmega32u4 -cavr109 -PCOM5 -b57600 -D -Uflash:w:"$(BinDir)\$(TargetName).hex":i 36 | Use Output window 37 | 38 | Pari kertaa GND->RST, sitten Tools->Arduino_via_bootloader 39 | C:\Users\lehti\Documents\Atmel Studio\7.0\CLASS_JOYSTICK1\CLASS_JOYSTICK1\Debug\CLASS_JOYSTICK1.hex 40 | 41 | Tulee "LUFA Joystick Demo", 2 nappia ja kolme axista (X,Y,Z). 42 | Näkyy Device managerissa "HID-compliant game controller". 43 | 0x03EB, 0x2043, Atmel Corp., LUFA Joystick Demo Application. 44 | 45 | VID/PID ja "LUFA Joystick Demo": Descriptors.c 46 | suuntien ja nappien määrä ehkä structissa: Joystick.h 47 | Ja myös: src/LUFA/Drivers/USB/Class/Common/HIDClassCommon.h (HID_DESCRIPTOR_JOYSTICK) 48 | Pois: HID_RI_USAGE(8, 0x32) 49 | Muokkaa: HID_RI_REPORT_COUNT(8, 3) -> 2 50 | 51 | Koodi: Joystick.c 52 | 53 | GitHub-integraation vois tehdä external tools:lla. 54 | 55 | PB6,2,3,1 56 | PB5,4 57 | ``` 58 | ## Joystick.c 59 | SetupHardware (before USB_INIT): 60 | ``` 61 | DDRB = 0x00; //PB0-7 -> input 62 | PORTB = 0xff; //PB0-7 -> pullup 63 | ``` 64 | 65 | CALLBACK_HID_Device_CreateHIDReport (before report size): 66 | ``` 67 | if (!(in & (1 << 6))) JoystickReport->Y = -100; 68 | if (!(in & (1 << 2))) JoystickReport->Y = 100; 69 | if (!(in & (1 << 3))) JoystickReport->X = -100; 70 | if (!(in & (1 << 1))) JoystickReport->X = 100; 71 | if (!(in & (1 << 5))) JoystickReport->Button |= (1 << 0); 72 | if (!(in & (1 << 4))) JoystickReport->Button |= (1 << 1); 73 | ``` 74 | -------------------------------------------------------------------------------- /C64_keyboard/C64-JoyKEY.sc: -------------------------------------------------------------------------------- 1 | # https://github.com/tebl/C64-JoyKEY 2 | # https://github.com/tebl/C64-JoyKEY/blob/main/documentation/schematic/C64%20Joykey.pdf 3 | # 4 | # http://kookye.com/wp-content/uploads/2016/02/Pinout-ProMicro.jpg 5 | # https://deskthority.net/wiki/Arduino_Pro_Micro 6 | # 7 | # PD1(D2) fire1 8 | # PD0(D3) fire2 9 | # PD4(D4) fire3 10 | # PB3(D14) left 11 | # PB1(D15) down 12 | # PF7(A0/D18) right 13 | # PF6(A1/D19) up 14 | # PD7(D6) underglowleds 15 | # PB6(D10) sysled 16 | # PB5(D9) powerled 17 | 18 | # '+' prefix = off at startup 19 | # '-' prefix = on at startup 20 | led caps -PD7 21 | led num +PB6 22 | led scroll -PB5 23 | 24 | matrix 25 | scanrate 1 26 | debounce 5 27 | blocking 1 28 | 29 | unstrobed -PD1 1 30 | unstrobed -PD0 2 31 | unstrobed -PD4 3 32 | unstrobed -PB3 L 33 | unstrobed -PB1 D 34 | unstrobed -PF7 R 35 | unstrobed -PF6 U 36 | end 37 | 38 | # Ultimate 64: fire 3 -> reset 39 | macroblock 40 | macro 3 -ALL 41 | MAKE LGUI 42 | MAKE LALT 43 | MAKE LSHIFT 44 | DELAY 50 45 | PRESS LCTRL 46 | DELAY 50 47 | BREAK LSHIFT 48 | BREAK LALT 49 | BREAK LGUI 50 | endmacro 51 | endblock 52 | 53 | # example, press a-button to CAPS_LOCK = toggle underglowleds 54 | #macroblock 55 | # macro A -ALL 56 | # PRESS CAPS_LOCK 57 | # CLEAR_ALL 58 | # endmacro 59 | #endblock 60 | 61 | # example, press b-button to NUM_LOCK = toggle SYS-led 62 | #macroblock 63 | # macro B -ALL 64 | # PRESS NUM_LOCK 65 | # CLEAR_ALL 66 | # endmacro 67 | #endblock 68 | 69 | # example, press c-button to SCROLL_LOCK = toggle PWR-led 70 | #macroblock 71 | # macro C -ALL 72 | # PRESS SCROLL_LOCK 73 | # CLEAR_ALL 74 | # endmacro 75 | #endblock 76 | 77 | # example, press d-button to get shifted D 78 | #macroblock 79 | # macro D -ALL 80 | # SET_META LSHIFT 81 | # PRESS D 82 | # CLEAR_ALL 83 | # endmacro 84 | #endblock 85 | 86 | # 87 | # unstrobed 88 | # Specifies a single pin to read and the corresponding HID code. 89 | # This is intended for a single switch which can be wired with the other side of the switch permanently connected to either ground or +5V. If the other side of the switch is at +5V, a pull-down resistor must be added between the pin and ground. 90 | # Example: 91 | # unstrobed -PF1 LSHIFT 92 | -------------------------------------------------------------------------------- /C64_keyboard/C64_joystick.sc: -------------------------------------------------------------------------------- 1 | # Matrix setup for Atari joysticks 2 | 3 | matrix 4 | scanrate 1 5 | debounce 5 6 | blocking 1 7 | 8 | # Joystick 1 (Joystick GND to Arduino GND) 9 | unstrobed -PD1 PAD_8 #pin_2 10 | unstrobed -PD0 PAD_2 #pin_3 11 | unstrobed -PD4 PAD_4 #pin_4 12 | unstrobed -PC6 PAD_6 #pin_5 13 | unstrobed -PD7 PAD_0 #pin_6 14 | 15 | # Joystick 2 (Joystick GND to Arduino GND) 16 | unstrobed -PF4 PAD_SLASH #pin_A3 17 | unstrobed -PF5 PAD_ASTERIX #pin_A2 18 | unstrobed -PF6 PAD_MINUS #pin_A1 19 | unstrobed -PF7 PAD_PLUS #pin_A0 20 | unstrobed -PB1 SCROLL_LOCK #pin_15 21 | end 22 | -------------------------------------------------------------------------------- /C64_keyboard/C64_matrix.sc: -------------------------------------------------------------------------------- 1 | # Matrix setup for COMMODORE 64 2 | 3 | #led caps PD0 4 | #led num PD1 5 | #led scroll PF0 6 | 7 | matrix 8 | scanrate 1 9 | debounce 5 10 | blocking 1 11 | 12 | sense PB6 PB2 PB3 PB1 PF7 PF6 PF5 PF4 PD3 13 | strobe PD1 1 BACK_QUOTE TAB ESC SPACE LCTRL Q 2 UNASSIGNED 14 | strobe PD0 3 W A LSHIFT Z S E 4 UNASSIGNED 15 | strobe PD4 5 R D X C F T 6 UNASSIGNED 16 | strobe PC6 7 Y G V B H U 8 UNASSIGNED 17 | strobe PD7 9 I J N M K O 0 UNASSIGNED 18 | strobe PE6 MINUS P L COMMA PERIOD SEMICOLON LEFT_BRACE EQUAL UNASSIGNED 19 | strobe PB4 INSERT RIGHT_BRACE QUOTE SLASH RSHIFT BACKSLASH DELETE HOME PAGE_UP 20 | strobe PB5 BACKSPACE ENTER RIGHT DOWN F1 F3 F5 F7 UNASSIGNED 21 | end 22 | 23 | # arrow left = BACK_QUOTE (section/fraction) 24 | # pound (£) = INSERT 25 | # restore = PAGE_UP 26 | # run/stop = ESC 27 | # commodore = LCTRL 28 | # arrow up = DELETE 29 | # asterisk (*) = RIGHT_BRACE 30 | # minus (-) = EQUAL 31 | # plus (+) = MINUS 32 | # at (@) = LEFT_BRACE 33 | # ctrl = TAB 34 | # lshift = LSHIFT 35 | # rshift = RSHIFT 36 | # equal (=) = BACKSLASH 37 | # colon (:) = SEMICOLON 38 | # semicolon (;) = QUOTE 39 | 40 | # circumvent TheC64 LSHIFT-LEFTARROW Menu-problem 41 | macroblock 42 | macro BACK_QUOTE LSHIFT 43 | CLEAR_META LSHIFT 44 | DELAY 100 45 | PRESS PAD_PERIOD 46 | endmacro 47 | endblock 48 | 49 | -------------------------------------------------------------------------------- /C64_keyboard/README.md: -------------------------------------------------------------------------------- 1 | # Poor man's "Keyrah" to connect C64 keyboard to USB (for BMC64 emulator) 2 | 3 | ![Controller](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/master/C64_keyboard/Soarer_controller_for_C64.jpg) 4 | 5 | ## Building 6 | - Solder all keyboard connector pins to Arduino Pro Micro. 7 | - You can solder Restore key (connector pin "I") paraller to some other pin. I soldered "I" and "G" to Arduino pin A8. (this is actually not needed, because there is one free pin left). [Pins I used](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/blob/master/C64_keyboard/README.md#pins-i-used). 8 | - You can also connect LED of C64 to VCC/GND of Arduino. I used 220ohm resistor. 9 | 10 | ## Firmware/flashing 11 | - Download Soarer controller firmware (Soarer_Controller_v1.20_beta4.zip): https://geekhack.org/index.php?topic=50437.0 (or https://deskthority.net/workshop-f7/soarer-s-keyboard-controller-firmware-t6767.html) 12 | - Extract /firmware/Soarer_Controller_v1.20_beta4_atmega32u4.hex from Soarer_Controller_v1.20_beta4.zip 13 | - Install Arduino IDE 14 | - Connect RST to GND couple of times to get Arduino Pro Micro to programming mode (notice that com-port is different in programming mode in Windows) 15 | - Flash firmware 16 | ``` 17 | # old: & "C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avrdude" -C"C:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf" -v -p m32u4 -c avr109 -P COM5 -b 57600 -U flash:w:firmware\Soarer_Controller_v1.20_beta4_atmega32u4.hex:i 18 | # 11.2.2023: 19 | & "$ENV:LOCALAPPDATA\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17\bin\avrdude.exe" -C"$ENV:LOCALAPPDATA\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17\etc\avrdude.conf" -v -p m32u4 -c avr109 -P COM9 -b 57600 -U flash:w:firmware\Soarer_Controller_v1.20_beta4_atmega32u4.hex:i 20 | ``` 21 | 22 | ## Setup 23 | - Download [config file](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/master/C64_keyboard/C64_matrix.sc). Check pin order to match your setup. 24 | - Compile and upload config file to Arduino Pro Micro 25 | ``` 26 | .\scas C64_matrix.sc C64_matrix.bin 27 | .\scwr C64_matrix.bin 28 | ``` 29 | 30 | 31 | ## Testing 32 | - https://keycode.info/ 33 | 34 | ## Pins I used 35 | ``` 36 | // Pin Label 37 | // 20(A) 2(D2/PD1) 38 | // 19(B) 3(D3/PD0) 39 | // 18(C) 4(D4/PD4) 40 | // 17(D) 5(D5/PC6) 41 | // 16(E) 6(D6/PD7) 42 | // 15(F) 7(D7/PE6) 43 | // 14(G) 8(D8/PB4) 44 | // 13(H) 9(D9/PB5) 45 | // 12(0) 10(D10/PB6) 46 | // 11(1) 16(D16/PB2) 47 | // 10(2) 14(D14/PB3) 48 | // 9(3) 15(D15/PB1) 49 | // 8(4) A0(A0/PF7) 50 | // 7(5) A1(A1/PF6) 51 | // 6(6) A2(A2/PF5) 52 | // 5(7) A3(A3/PF4) 53 | // 4 N/C 54 | // 3(8) TX(D1/PD3) 55 | // 2 N/C 56 | // 1(I) 8(D8/PB4) 57 | ``` 58 | ## Links 59 | 60 | - https://geekhack.org/index.php?topic=50437.0 61 | - https://deskthority.net/workshop-f7/soarer-s-keyboard-controller-firmware-t6767.html 62 | - Newest I found was Soarer_Controller_v1.20_beta4.zip from 26.10.2013 63 | - Soarer config file documentation: https://deskthority.net/download/file.php?id=8833 (Soarer_Converter_v1.12_docs.zip) 64 | - Keynames used in config file from https://deskthority.net/download/file.php?id=8833 (Soarer_Converter_v1.12_docs.zip): /docs/codes.html 65 | - https://www.waitingforfriday.com/wp-content/uploads/2017/01/C64_Keyboard_Schematics_PNG.png 66 | - http://kookye.com/wp-content/uploads/2016/02/Pinout-ProMicro.jpg 67 | - I used this as starting point: https://github.com/abzman/Keyboard-config-file/blob/master/C64_matrix.sc 68 | - C64-emulator for Raspberry Pi 2/3: http://accentual.com/bmc64/ 69 | - For bigger matrices use Arduino Micro (24 usable IO pins) https://www.40percent.club/2017/10/green-arduino-micro.html (The largest matrix you can do with a Pro Micro with its 18 pins is 9x9, 81 keys.) 70 | - Example macros: https://sharktastica.co.uk/guides/soarers_2 71 | 72 | ## Macro without modifier key 73 | Even though modifier key is mandatory, it can be replaced with unpressed modifiers. This writes shifted A when only a-key is pressed: 74 | ``` 75 | macroblock 76 | macro A -ALL 77 | SET_META LSHIFT 78 | PRESS A 79 | CLEAR_ALL 80 | endmacro 81 | endblock 82 | ``` 83 | 84 | ## TheC64 Maxi Orange Pi PC 85 | Buttons that needs modification: 86 | ``` 87 | + 12 88 | - 13 89 | @ 26 90 | * 27 91 | ^ 111 92 | : 39 93 | ; 40 94 | = 43 95 | £ 110 96 | C= 29 97 | CTRL 15 98 | RESTORE 104 99 | INS/DEL 14 100 | ``` 101 | Copy theC64-sym-CLASSIC.vkm to /usr/lib/vice/C64/theC64-sym-CLASSIC.vkm in ext4 partition of firmware-image. (THEC64_for_OPI.img or THE_VIC_20_for_OPI.img) 102 | 103 | ## OBSOLETE: Issues (all issues are fixed in newer BMC64 versions) 104 | 105 | ### Issue with Menu key (fixed in newer BMC64 versions) 106 | - .crt-files doesn't work with C= + F7, so as workaround LSHIFT + F7 is F12. 107 | 108 | ### Issue with shift (fixed in newer BMC64 versions) 109 | 110 | - "+", "-", "£", "@" and "*" doesn't produce GFX-chars when shifted 111 | 112 | #### "Repair" rpi_sym.vkm (1.0.9)? 113 | - at 5 6 0 -> 5 6 8 114 | - minus 5 3 0 -> 5 3 8 115 | - plus 5 0 0 -> 5 0 8 116 | - sterling 6 0 0 -> 6 0 8 117 | - asterisk 6 1 0 -> 6 1 8 118 | ``` 119 | sed -i 's/5 6 0/5 6 8/g; s/5 3 0/5 3 8/g; s/5 0 0/5 0 8/g; s/6 0 0/6 0 8/g; s/6 1 0/6 1 8/g' rpi_sym.vkm 120 | ``` 121 | 122 | ## Stuff 123 | Ultimate keybindings? 124 | ``` 125 | F9 = shift+return 126 | F10 = Ultimate menu 127 | F11 = Freeze 128 | F12 = Restore 129 | LCTRL = Control 130 | LWIN = CBM 131 | INS = Inst 132 | HOME = Clr/home 133 | PAUSE = run/stop 134 | SCROLL_LOCK = Ultimate menu 135 | BACKSPACE = Inst/del 136 | LSHIFT+LCTRL+LWIN+LALT = Reset 137 | 138 | Buttons: Freeze-Menu-Reset 139 | ``` 140 | -------------------------------------------------------------------------------- /C64_keyboard/Soarer_controller_for_C64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/C64_keyboard/Soarer_controller_for_C64.jpg -------------------------------------------------------------------------------- /C64_keyboard/theC64-sym-CLASSIC.vkm: -------------------------------------------------------------------------------- 1 | # C64 mode, edited for BMC64 keyboard by McGurk 2 | 3 | !CLEAR 4 | !LSHIFT 1 7 5 | !RSHIFT 6 4 6 | !VSHIFT RSHIFT 7 | !SHIFTL LSHIFT 8 | 9 | # Regular codes 10 | 41 7 1 8 /* <- */ 11 | 2 7 0 8 /* 1 */ 12 | 3 7 3 8 /* 2 */ 13 | 4 1 0 8 /* 3 */ 14 | 5 1 3 8 /* 4 */ 15 | 6 2 0 8 /* 5 */ 16 | 7 2 3 8 /* 6 */ 17 | 8 3 0 8 /* 7 */ 18 | 9 3 3 8 /* 8 */ 19 | 10 4 0 8 /* 9 */ 20 | 11 4 3 8 /* 0 */ 21 | #78 5 0 8 /* + */ 22 | #74 5 3 8 /* - */ 23 | #12 6 0 8 /* £ */ 24 | 12 5 0 8 /* + */ 25 | 13 5 3 8 /* - */ 26 | 110 6 0 8 /* £ */ 27 | 102 6 3 8 /* CLR/HOME */ 28 | 14 0 0 8 /* INST/DEL */ 29 | 30 | 29 7 5 8 /* (CTRL 29 7 2 8) Control_L -> CMD */ 31 | 56 7 5 8 /* (-) Alt_L -> CMD */ 32 | 16 7 6 8 /* Q */ 33 | 17 1 1 8 /* W */ 34 | 18 1 6 8 /* E */ 35 | 19 2 1 8 /* R */ 36 | 20 2 6 8 /* T */ 37 | 21 3 1 8 /* Y */ 38 | 22 3 6 8 /* U */ 39 | 23 4 1 8 /* I */ 40 | 24 4 6 8 /* O */ 41 | 25 5 1 8 /* P */ 42 | #43 5 6 8 /* @ */ 43 | #39 6 1 8 /* * */ 44 | #40 6 6 8 /* ^ */ 45 | 26 5 6 8 /* @ */ 46 | 27 6 1 8 /* * */ 47 | 111 6 6 8 /* ^ */ 48 | 15 7 2 8 /* (15 -3 0 RESTORE) Tab -> CTRL */ 49 | 104 -3 0 /* (-) PageUp -> (RESTORE) */ 50 | 51 | 1 7 7 8 /* RUN/STOP */ 52 | 30 1 2 8 /* A */ 53 | 31 1 5 8 /* S */ 54 | 32 2 2 8 /* D */ 55 | 33 2 5 8 /* F */ 56 | 34 3 2 8 /* G */ 57 | 35 3 5 8 /* H */ 58 | 36 4 2 8 /* J */ 59 | 37 4 5 8 /* K */ 60 | 38 5 2 8 /* L */ 61 | #26 5 5 8 /* : */ 62 | #27 6 2 8 /* ; */ 63 | #13 6 5 8 /* = */ 64 | 39 5 5 8 /* : */ 65 | 40 6 2 8 /* ; */ 66 | 43 6 5 8 /* = */ 67 | 28 0 1 8 /* RETURN */ 68 | 69 | 125 7 5 8 /* THEC64 */ 70 | 42 1 7 2 /* SHIFT (left) */ 71 | 44 1 4 8 /* Z */ 72 | 45 2 7 8 /* X */ 73 | 46 2 4 8 /* C */ 74 | 47 3 7 8 /* V */ 75 | 48 3 4 8 /* B */ 76 | 49 4 7 8 /* N */ 77 | 50 4 4 8 /* M */ 78 | 51 5 7 8 /* , */ 79 | 52 5 4 8 /* . */ 80 | 53 6 7 8 /* / */ 81 | 54 6 4 4 /* SHIFT (right) */ 82 | 108 0 7 8 /* CRSR vert */ 83 | 106 0 2 8 /* CRSR horiz */ 84 | 85 | 57 7 4 8 /* (space) */ 86 | 87 | 59 0 4 8 /* F1 */ 88 | 61 0 5 8 /* F3 */ 89 | 63 0 6 8 /* F5 */ 90 | 65 0 3 8 /* F7 */ 91 | 92 | # Special codes for joystick buttons and virtual keyboard 93 | 150 6 0 8 /* £ */ 94 | 151 7 6 8 /* Q */ 95 | 152 1 1 8 /* W */ 96 | 153 3 1 8 /* Y */ 97 | 154 5 6 8 /* @ */ 98 | 155 6 6 8 /* ^ */ 99 | 156 1 2 8 /* A */ 100 | 157 5 5 8 /* : */ 101 | 158 6 2 8 /* ; */ 102 | 159 1 4 8 /* Z */ 103 | 160 4 4 8 /* M */ 104 | 161 7 0 1 /* ! */ 105 | 162 7 3 1 /* " */ 106 | 163 1 0 1 /* # */ 107 | 164 1 3 1 /* $ */ 108 | 165 2 0 1 /* % */ 109 | 166 2 3 1 /* & */ 110 | 167 3 0 1 /* ' */ 111 | 168 3 3 1 /* ( */ 112 | 169 4 0 1 /* ) */ 113 | 170 5 5 1 /* [ */ 114 | 171 6 2 1 /* ] */ 115 | 172 5 7 1 /* < */ 116 | 173 5 4 1 /* > */ 117 | 174 6 7 1 /* ? */ 118 | 175 6 6 1 /* Pi */ 119 | 120 | 55 6 1 8 /* * */ 121 | 83 5 4 8 /* . */ 122 | 121 5 7 8 /* , */ 123 | 98 6 7 8 /* / */ 124 | 117 6 5 8 /* = */ 125 | 82 4 3 8 /* 0 */ 126 | 79 7 0 8 /* 1 */ 127 | 80 7 3 8 /* 2 */ 128 | 81 1 0 8 /* 3 */ 129 | 75 1 3 8 /* 4 */ 130 | 76 2 0 8 /* 5 */ 131 | 77 2 3 8 /* 6 */ 132 | 71 3 0 8 /* 7 */ 133 | 72 3 3 8 /* 8 */ 134 | 73 4 0 8 /* 9 */ 135 | 136 | 60 0 4 1 /* F2 */ 137 | 62 0 5 1 /* F4 */ 138 | 64 0 6 1 /* F6 */ 139 | 66 0 3 1 /* F8 */ 140 | 141 | 103 0 7 1 /* up */ 142 | 105 0 2 1 /* left */ 143 | 144 | #110 0 0 1 /* insert */ 145 | #111 0 0 8 /* delete */ 146 | -------------------------------------------------------------------------------- /Images/Arduino_ProMicro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/Arduino_ProMicro.jpg -------------------------------------------------------------------------------- /Images/Levelconverter_with_AMS1117.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/Levelconverter_with_AMS1117.jpg -------------------------------------------------------------------------------- /Images/USB_Host_Shield_DuinoFun_UHS_mini_v2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/USB_Host_Shield_DuinoFun_UHS_mini_v2.jpg -------------------------------------------------------------------------------- /Images/Windows_Game_Controller_properties.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/Windows_Game_Controller_properties.jpg -------------------------------------------------------------------------------- /Images/ps2-keyboard-adapter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/ps2-keyboard-adapter.jpg -------------------------------------------------------------------------------- /Images/sega_genesis_adapter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/sega_genesis_adapter.jpg -------------------------------------------------------------------------------- /Images/self_made_NES_connector_arrangement.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/self_made_NES_connector_arrangement.jpg -------------------------------------------------------------------------------- /Images/usb-shield-pinout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/usb-shield-pinout.jpg -------------------------------------------------------------------------------- /Images/x-arcade-dual-joystick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Images/x-arcade-dual-joystick.jpg -------------------------------------------------------------------------------- /Keyboard_PS2/PS2Keyboard_mcgurk.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/Keyboard_PS2/PS2Keyboard_mcgurk.zip -------------------------------------------------------------------------------- /Keyboard_PS2/README.md: -------------------------------------------------------------------------------- 1 | ## PS/2 keyboard as 4 USB-joysticks 2 | 3 | ![PS2-adapter](https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/master/Images/ps2-keyboard-adapter.jpg) 4 | 5 | Needs Atmega32u4 (e.g. Arduino Leonardo). 6 | 7 | PS/2 is 5V. 8 | 9 | Needs custom ps2-library (PS2Keyboard_mcgurk.zip, unzip to libraries-folder). 10 | 11 | Clock-signal must be in pin 2 (because of interrupts). 12 | 13 | Edit button assignments manually. Assignments are in the end of source file. 14 | 15 | `#define DEBUG` if you want to see codes in console. 16 | 17 | Joystick 0: 18 | ``` 19 | arrows = up/down/left/right 20 | R-ctrl = a 21 | alt gr = b 22 | enter = start 23 | R-shift = select 24 | ``` 25 | 26 | Joystick 1: 27 | ``` 28 | W/S/A/D = up/down/left/right 29 | L-ctrl = a 30 | L-alt = b 31 | tab = start 32 | L-shift = select 33 | ``` 34 | 35 | Joystick 2: 36 | ``` 37 | I/K/J/L = up/down/left/right 38 | M = a 39 | N = b 40 | O = start 41 | U = select 42 | ``` 43 | 44 | Joystick 3: 45 | ``` 46 | keypad 8/5/4/6 = up/down/left/right 47 | keypad 0 = a 48 | keypad , = b 49 | keypad enter = start 50 | keypad + = select 51 | ``` 52 | 53 | ## Links 54 | 55 | http://playground.arduino.cc/Main/PS2Keyboard 56 | 57 | http://www.computer-engineering.org/ps2protocol/ 58 | 59 | http://www.computer-engineering.org/ps2keyboard/scancodes2.html 60 | -------------------------------------------------------------------------------- /Keyboard_PS2/RetroJoystickAdapter_PS2-keyboard.ino: -------------------------------------------------------------------------------- 1 | #include "HID.h" 2 | 3 | #if ARDUINO < 10606 4 | #error The Joystick2 library requires Arduino IDE 1.6.6 or greater. Please update your IDE. 5 | #endif 6 | 7 | #if !defined(USBCON) 8 | #error The Joystick2 library can only be used with a USB MCU (e.g. Arduino Leonardo, Arduino Micro, etc.). 9 | #endif 10 | 11 | #if !defined(_USING_HID) 12 | #error "legacy HID core (non pluggable)" 13 | #endif 14 | 15 | #define JOYSTICK_REPORT_ID 0x03 16 | #define JOYSTICK2_REPORT_ID 0x04 17 | #define JOYSTICK3_REPORT_ID 0x05 18 | #define JOYSTICK4_REPORT_ID 0x06 19 | 20 | #define JOYSTICK_DATA_SIZE 2 21 | #define JOYSTICK_STATE_SIZE 3 22 | 23 | 24 | #include 25 | 26 | const int DataPin = 3; // PS/2 pin 1 27 | const int IRQpin = 2; // PS/2 pin 5 28 | 29 | PS2Keyboard keyboard; 30 | 31 | //#define DEBUG 32 | 33 | //================================================================================ 34 | //================================================================================ 35 | // Joystick (Gamepad) 36 | 37 | 38 | #define HIDDESC_MACRO(REPORT_ID) \ 39 | /* Joystick # */ \ 40 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 41 | 0x09, 0x04, /* USAGE (Joystick) */ \ 42 | 0xa1, 0x01, /* COLLECTION (Application) */ \ 43 | 0x85, REPORT_ID, /* REPORT_ID */ \ 44 | /* 8 Buttons */ \ 45 | 0x05, 0x09, /* USAGE_PAGE (Button) */ \ 46 | 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ \ 47 | 0x29, 0x08, /* USAGE_MAXIMUM (Button 8) */ \ 48 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 49 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \ 50 | 0x75, 0x01, /* REPORT_SIZE (1) */ \ 51 | 0x95, 0x08, /* REPORT_COUNT (8) */ \ 52 | 0x55, 0x00, /* UNIT_EXPONENT (0) */ \ 53 | 0x65, 0x00, /* UNIT (None) */ \ 54 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 55 | /* X and Y Axis */ \ 56 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 57 | 0x09, 0x01, /* USAGE (Pointer) */ \ 58 | 0xA1, 0x00, /* COLLECTION (Physical) */ \ 59 | 0x09, 0x30, /* USAGE (x) */ \ 60 | 0x09, 0x31, /* USAGE (y) */ \ 61 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 62 | 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ \ 63 | 0x75, 0x08, /* REPORT_SIZE (8) */ \ 64 | 0x95, 0x02, /* REPORT_COUNT (2) */ \ 65 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 66 | 0xc0, /* END_COLLECTION */ \ 67 | 0xc0 /* END_COLLECTION */ 68 | 69 | 70 | 71 | 72 | static const uint8_t hidReportDescriptor[] PROGMEM = { 73 | HIDDESC_MACRO(JOYSTICK_REPORT_ID), 74 | HIDDESC_MACRO(JOYSTICK2_REPORT_ID), 75 | HIDDESC_MACRO(JOYSTICK3_REPORT_ID), 76 | HIDDESC_MACRO(JOYSTICK4_REPORT_ID) 77 | }; 78 | 79 | 80 | class Joystick_ { 81 | 82 | private: 83 | uint8_t joystickId; 84 | uint8_t reportId; 85 | uint8_t state[JOYSTICK_STATE_SIZE]; 86 | uint8_t flag; 87 | 88 | public: 89 | uint8_t type; 90 | uint8_t data[JOYSTICK_DATA_SIZE]; 91 | 92 | Joystick_(uint8_t initJoystickId, uint8_t initReportId) { 93 | // Setup HID report structure 94 | static bool usbSetup = false; 95 | 96 | if (!usbSetup) { 97 | static HIDSubDescriptor node(hidReportDescriptor, sizeof(hidReportDescriptor)); 98 | HID().AppendDescriptor(&node); 99 | usbSetup = true; 100 | } 101 | 102 | // Initalize State 103 | joystickId = initJoystickId; 104 | reportId = initReportId; 105 | 106 | data[0] = 0; 107 | data[1] = 0; 108 | updateState(); 109 | sendState(1); 110 | } 111 | 112 | void updateState() { 113 | state[0] = data[0]; 114 | state[1] = 127; 115 | state[2] = 127; 116 | if (bitRead(data[1], 0)) state[2] = 0; /* up */ 117 | if (bitRead(data[1], 1)) state[2] = 255; /* down */ 118 | if (bitRead(data[1], 2)) state[1] = 0; /* left */ 119 | if (bitRead(data[1], 3)) state[1] = 255; /* right */ 120 | } 121 | 122 | void sendState(uint8_t force = 0) { 123 | if (flag || force) { 124 | // HID().SendReport(Report number, array of values in same order as HID descriptor, length) 125 | HID().SendReport(reportId, state, JOYSTICK_STATE_SIZE); 126 | flag = 0; 127 | } 128 | } 129 | 130 | }; 131 | 132 | 133 | Joystick_ Joystick[4] = 134 | { 135 | Joystick_(0, JOYSTICK_REPORT_ID), 136 | Joystick_(1, JOYSTICK2_REPORT_ID), 137 | Joystick_(2, JOYSTICK3_REPORT_ID), 138 | Joystick_(3, JOYSTICK4_REPORT_ID) 139 | }; 140 | 141 | //================================================================================ 142 | //================================================================================ 143 | 144 | 145 | 146 | void setup() { 147 | delay(1000); 148 | keyboard.begin(DataPin, IRQpin); 149 | #ifdef DEBUG 150 | Serial.begin(9600); 151 | Serial.println("Start"); 152 | #endif 153 | } 154 | 155 | 156 | void set(uint32_t code, uint8_t j, uint8_t d, uint8_t b) { 157 | uint8_t is_break = 0; 158 | if ( (code >> 8 & 0xff) == 0xF0 ) is_break = 1; 159 | if (is_break) bitClear(Joystick[j].data[d], b); 160 | if (!is_break) bitSet(Joystick[j].data[d], b); 161 | Joystick[j].updateState(); 162 | Joystick[j].sendState(1); 163 | } 164 | 165 | void loop() { 166 | if (keyboard.available()) { 167 | 168 | // read whole code at once 169 | int32_t code = keyboard.read(); 170 | uint8_t c = code & 0xff; 171 | uint8_t e; 172 | //if ( (code & 0xff) == 0xE0 || (code >> 8 & 0xff) == 0xE0 || (code >> 16 & 0xff) == 0xE0 || (code >> 24 & 0xff) == 0xE0 ) e = 1; 173 | if ( (code >> 8 & 0xff) == 0xE0 || (code >> 16 & 0xff) == 0xE0 ) e = 1; else e = 0; 174 | //if (code^0xE000 || code^0xE00000) e = 1; 175 | 176 | #ifdef DEBUG 177 | Serial.print("e:");Serial.print(e);Serial.print(" "); 178 | Serial.println(code, HEX); 179 | #endif 180 | 181 | //http://www.computer-engineering.org/ps2keyboard/scancodes2.html 182 | //set-function parameters: code, joystick (0-3), data (0 or 1), bit (0-7) 183 | // if e is 1, it means codes with E0 prefix 184 | 185 | //Joystick 0 186 | if (e == 1 && c == 0x75) set(code, 0, 1, 0); // up 187 | if (e == 1 && c == 0x72) set(code, 0, 1, 1); // down 188 | if (e == 1 && c == 0x6B) set(code, 0, 1, 2); // left 189 | if (e == 1 && c == 0x74) set(code, 0, 1, 3); // right 190 | if (e == 1 && c == 0x14) set(code, 0, 0, 0); // R-ctrl = a 191 | if (e == 1 && c == 0x11) set(code, 0, 0, 1); // alt gr = b 192 | if (e == 0 && c == 0x5A) set(code, 0, 0, 2); // enter = start 193 | if (e == 0 && c == 0x59) set(code, 0, 0, 3); // R-shift = select 194 | //if (e == x && c == 0xXX) set(code, 0, 0, 4); // = x 195 | //if (e == x && c == 0xXX) set(code, 0, 0, 5); // = y 196 | //if (e == x && c == 0xXX) set(code, 0, 0, 6); // = left shoulder 197 | //if (e == x && c == 0xXX) set(code, 0, 0, 7); // = right shoulder 198 | 199 | //Joystick 1 200 | if (e == 0 && c == 0x1D) set(code, 1, 1, 0); // w = up 201 | if (e == 0 && c == 0x1B) set(code, 1, 1, 1); // s = down 202 | if (e == 0 && c == 0x1C) set(code, 1, 1, 2); // a = left 203 | if (e == 0 && c == 0x23) set(code, 1, 1, 3); // d = right 204 | if (e == 0 && c == 0x14) set(code, 1, 0, 0); // L-ctrl = a 205 | if (e == 0 && c == 0x11) set(code, 1, 0, 1); // L-alt = b 206 | if (e == 0 && c == 0x0D) set(code, 1, 0, 2); // tab = start 207 | if (e == 0 && c == 0x12) set(code, 1, 0, 3); // L-shift = select 208 | //if (e == x && c == 0xXX) set(code, 1, 0, 4); // = x 209 | //if (e == x && c == 0xXX) set(code, 1, 0, 5); // = y 210 | //if (e == x && c == 0xXX) set(code, 1, 0, 6); // = left shoulder 211 | //if (e == x && c == 0xXX) set(code, 1, 0, 7); // = right shoulder 212 | 213 | //Joystick 2 214 | if (e == 0 && c == 0x43) set(code, 2, 1, 0); // i = up 215 | if (e == 0 && c == 0x42) set(code, 2, 1, 1); // k = down 216 | if (e == 0 && c == 0x3B) set(code, 2, 1, 2); // j = left 217 | if (e == 0 && c == 0x4B) set(code, 2, 1, 3); // l = right 218 | if (e == 0 && c == 0x3A) set(code, 2, 0, 0); // m = a 219 | if (e == 0 && c == 0x31) set(code, 2, 0, 1); // n = b 220 | if (e == 0 && c == 0x44) set(code, 2, 0, 2); // o = start 221 | if (e == 0 && c == 0x3C) set(code, 2, 0, 3); // u = select 222 | //if (e == x && c == 0xXX) set(code, 2, 0, 4); // = x 223 | //if (e == x && c == 0xXX) set(code, 2, 0, 5); // = y 224 | //if (e == x && c == 0xXX) set(code, 2, 0, 6); // = left shoulder 225 | //if (e == x && c == 0xXX) set(code, 2, 0, 7); // = right shoulder 226 | 227 | //Joystick 3 228 | if (e == 0 && c == 0x75) set(code, 3, 1, 0); // keypad 8 229 | if (e == 0 && c == 0x73) set(code, 3, 1, 1); // keypad 5 230 | if (e == 0 && c == 0x6B) set(code, 3, 1, 2); // keypad 4 231 | if (e == 0 && c == 0x74) set(code, 3, 1, 3); // keypad 6 232 | if (e == 0 && c == 0x70) set(code, 3, 0, 0); // keypad 0 = a 233 | if (e == 0 && c == 0x71) set(code, 3, 0, 1); // keypad , = b 234 | if (e == 1 && c == 0x5A) set(code, 3, 0, 2); // keypad enter = start 235 | if (e == 0 && c == 0x79) set(code, 3, 0, 3); // keypad + = select 236 | //if (e == x && c == 0xXX) set(code, 3, 0, 4); // = x 237 | //if (e == x && c == 0xXX) set(code, 3, 0, 5); // = y 238 | //if (e == x && c == 0xXX) set(code, 3, 0, 6); // = left shoulder 239 | //if (e == x && c == 0xXX) set(code, 3, 0, 7); // = right shoulder 240 | 241 | } 242 | 243 | } 244 | -------------------------------------------------------------------------------- /PS2_Soarer_Converter/README.md: -------------------------------------------------------------------------------- 1 | # Soarer PS/2 to USB keyboard converter 2 | https://deskthority.net/workshop-f7/xt-at-ps2-terminal-to-usb-converter-with-nkro-t2510.html 3 | 4 | #### Keyboard protocols supported: 5 | - XT (scan code set 1) 6 | - AT (scan code set 2) 7 | - PS/2 (MF2) (extended scan code set 2) 8 | - Terminal e.g. 3179/318x/319x (scan code set 3) 9 | #### Configurable Features: 10 | - Remapping 11 | - Layers 12 | - Macros 13 | - On-the-fly Config Selection 14 | #### Other Features: 15 | - Full NKRO, if the keyboard supports it (even on Macs!) 16 | - Boot mode support (even with faulty BIOS!) 17 | - Auto-detection of the keyboard type 18 | - XT and AT boards are remapped correctly for PrtSc etc. 19 | - 1000Hz polling using Full Speed USB 20 | - Suspend and resume support 21 | - Media and Power key support 22 | - Jump to bootloader function (update firmware without pressing the reset button) (v1.0+) 23 | 24 | 25 | 26 | ## Hardware 27 | You need 28 | - ATMega32U4 Microcontroller/Arduino. E.g. Arduino Pro Micro 29 | - PS/2 female connector 30 | 31 | PS/2 -> Arduino 32 | - Data (green or blue) -> PD0 (Pro Micro: 3) 33 | - CLK (white or purple) -> PD1 (Pro Micro: 2) 34 | - 5V (red) -> 5V 35 | - GND (black) -> GND 36 | 37 | ##### Arduino Pro Micro pinout 38 | http://www.pighixxx.com/test/wp-content/uploads/2016/07/pro_micro_pinout_v1_0_red.png 39 | 40 | ##### PS/2 pinout 41 | http://ezcontents.org/sites/default/files/ps2_pinout.png 42 | 43 | ## Firmware 44 | https://deskthority.net/workshop-f7/xt-at-ps2-terminal-to-usb-converter-with-nkro-t2510.html 45 | 46 | Soarer_Converter_v1.12_update.zip: https://deskthority.net/resources/file/8295 47 | 48 | #### Flasher 49 | https://sourceforge.net/projects/winavr/ 50 | #### Flashing 51 | ``` 52 | avrdude -p m32u4 -b 57600 -P com5 -c avr109 -U flash:w:Soarer_at2usb_v1.12_atmega32u4.hex 53 | ``` 54 | Bootloader mode: RST to GND two times. You have couple of seconds to start flashing. Serial port can be different than in normal mode. 55 | 56 | 57 | ## Testing/Debugging 58 | hid_listen.exe: https://www.pjrc.com/teensy/hid_listen.html 59 | 60 | ## Settings/Tools/Docs 61 | Tools (Soarer_Converter_v1.10.zip): 62 | - https://deskthority.net/workshop-f7/xt-at-ps2-terminal-to-usb-converter-with-nkro-t2510.html 63 | - https://deskthority.net/download/file.php?id=6142 64 | 65 | Docs (Soarer_Converter_v1.12_docs.zip): 66 | - https://deskthority.net/workshop-f7/xt-at-ps2-terminal-to-usb-converter-with-nkro-t2510.html 67 | - https://deskthority.net/download/file.php?id=8833 68 | - Key names/codes: docs/codes.html 69 | 70 | Tools from Soarer_Controller_v1.20_beta4.zip doesn't work!: 71 | ``` 72 | protocol version check: converter=1.00, scwr=1.00: ok 73 | settings version check: converter=1.01, file=1.03: failed 74 | ``` 75 | 76 | Compile text file to binary 77 | ``` 78 | scas xarcade.txt xarcade.bin 79 | ``` 80 | Upload config (effective immediately) 81 | ``` 82 | scwr xarcade.bin 83 | ``` 84 | 85 | For deleting all settings, make empty file and assemble and upload it. 86 | 87 | ### Compile tools with Linux (e.g. Raspberry Pi, Orange Pi) 88 | ``` 89 | sudo apt install build-essential 90 | unzip Soarer_Converter_v1.10.zip 91 | cd tools 92 | unzip Soarer_sctools_v1.10_source.zip 93 | cd build/linux 94 | make 95 | ``` 96 | 97 | 98 | ## Links 99 | - https://geekhack.org/index.php?topic=17458.0 100 | - http://www.computer-engineering.org/ps2protocol/ 101 | - http://www.computer-engineering.org/ps2keyboard/ 102 | - http://www.computer-engineering.org/ps2keyboard/scancodes2.html 103 | - test: https://www.microsoft.com/appliedsciences/KeyboardGhostingDemo.mspx 104 | - test: https://keycode.info/ 105 | 106 | ## Keyboard controller 107 | - https://geekhack.org/index.php?topic=50437.0 108 | - https://deskthority.net/workshop-f7/soarer-s-keyboard-controller-firmware-t6767.html 109 | Soarer_Controller_v1.20_beta4.zip 110 | 111 | 112 | ## X-Arcade (xarcade.txt) 113 | ``` 114 | # scas xarcade.txt xarcade.bin 115 | # scwr xarcade.bin 116 | 117 | # important!: 118 | force set2ext 119 | 120 | # dir1 keypad 8 (75),2 (72),4 (6B),6 (74) 121 | # sel1 3 122 | # start1 1 123 | # A,B,C: left shift (12),Z,X 124 | # X,Y,Z: left ctrl (14),left alt (11),space (29) 125 | # lowerbuttons: c,5 126 | 127 | remapblock 128 | # left controller 129 | PAD_8 W 130 | PAD_2 S 131 | PAD_4 A 132 | PAD_6 D 133 | LSHIFT SPACE 134 | 135 | Z F 136 | X G 137 | LCTRL R 138 | LALT T 139 | SPACE Y 140 | C V # "normal" 141 | # C RCTRL # "commando" 142 | 5 B 143 | 3 Q 144 | 1 E 145 | 146 | # right controller 147 | R PAD_8 148 | F PAD_2 149 | D PAD_4 150 | G PAD_6 151 | W RCTRL 152 | 153 | E K 154 | LEFT_BRACE L 155 | A I 156 | S O 157 | Q P 158 | RIGHT_BRACE N # "normal" 159 | # RIGHT_BRACE SPACE # "commando" 160 | 6 M 161 | 2 U 162 | 4 J 163 | endblock 164 | 165 | # dir2 r,f,d,g 166 | # sel2 4 167 | # start2 2 168 | # A,B,C: w,e,}(å)(54="[") 169 | # X,Y,Z: a,s,q 170 | # lowerbuttons: ;(¨)(5B="]"),6 171 | ``` 172 | 173 | 174 | ## X-Arcade BMC64 emulator 175 | - [xarcade_bmc64.txt](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/blob/master/PS2_Soarer_Converter/xarcade_bmc64.txt) 176 | 177 | ## IBM model F XT 178 | 179 | Colors: https://geekhack.org/index.php?topic=17458.msg616598#msg616598 180 | ``` 181 | Brown = Vcc 182 | Red = Ground 183 | White = Data 184 | Black = Clock 185 | ``` 186 | 187 | Soarer_Converter_v1.12_docs/docs/hardware.html/hardware.html 188 | ``` 189 | GND GND 190 | Vcc/+5V VCC 191 | Data PD0 192 | Clock PD1 193 | ``` 194 | 195 | Conlusion: 196 | ``` 197 | Brown = Vcc (5) -> VCC 198 | Red = Ground (4) -> GND 199 | White = Data (2) -> PD0 (Pro Micro: 3) 200 | Black = Clock (1) -> PD1 (Pro Micro: 2) 201 | ``` 202 | 203 | ## X-Arcade With Mister FPGA 204 | - [xarcade_mister.txt](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/blob/master/PS2_Soarer_Converter/xarcade_mister.txt) 205 | -------------------------------------------------------------------------------- /PS2_Soarer_Converter/Soarer_Converter_v1.10.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/PS2_Soarer_Converter/Soarer_Converter_v1.10.zip -------------------------------------------------------------------------------- /PS2_Soarer_Converter/Soarer_Converter_v1.12_update.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/PS2_Soarer_Converter/Soarer_Converter_v1.12_update.zip -------------------------------------------------------------------------------- /PS2_Soarer_Converter/arduino_pro_micro_soarer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/PS2_Soarer_Converter/arduino_pro_micro_soarer.jpg -------------------------------------------------------------------------------- /PS2_Soarer_Converter/empty.txt: -------------------------------------------------------------------------------- 1 | # scas empty.txt empty.bin 2 | # scwr empty.bin 3 | -------------------------------------------------------------------------------- /PS2_Soarer_Converter/rawhid.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/PS2_Soarer_Converter/rawhid.dll -------------------------------------------------------------------------------- /PS2_Soarer_Converter/scas.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/PS2_Soarer_Converter/scas.exe -------------------------------------------------------------------------------- /PS2_Soarer_Converter/scwr.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/PS2_Soarer_Converter/scwr.exe -------------------------------------------------------------------------------- /PS2_Soarer_Converter/xarcade.txt: -------------------------------------------------------------------------------- 1 | # scas xarcade.txt xarcade.bin 2 | # scwr xarcade.bin 3 | force set2ext 4 | 5 | # dir1 keypad 8 (75),2 (72),4 (6B),6 (74) 6 | # sel1 3 7 | # start1 1 8 | # A,B,C: vas shift (12),Z,X 9 | # X,Y,Z: vas ctrl (14),vas alt (11),space (29) 10 | # alanapit: c,5 11 | 12 | remapblock 13 | # left controller 14 | PAD_8 W 15 | PAD_2 S 16 | PAD_4 A 17 | PAD_6 D 18 | LSHIFT SPACE 19 | 20 | Z F 21 | X G 22 | LCTRL R 23 | LALT T 24 | SPACE Y 25 | C V # "normal" 26 | # C RCTRL # "commando" 27 | 5 B 28 | 3 Q 29 | 1 E 30 | 31 | # right controller 32 | R PAD_8 33 | F PAD_2 34 | D PAD_4 35 | G PAD_6 36 | W RCTRL 37 | 38 | E K 39 | LEFT_BRACE L 40 | A I 41 | S O 42 | Q P 43 | RIGHT_BRACE N # "normal" 44 | # RIGHT_BRACE SPACE # "commando" 45 | 6 M 46 | 2 U 47 | 4 J 48 | endblock 49 | 50 | # dir2 r,f,d,g 51 | # sel2 4 52 | # start2 2 53 | # A,B,C: w,e,}(å)(54="[") 54 | # X,Y,Z: a,s,q 55 | # alanapit: ;(¨)(5B="]"),6 56 | -------------------------------------------------------------------------------- /PS2_Soarer_Converter/xarcade_bmc64.txt: -------------------------------------------------------------------------------- 1 | # scas xarcade_bmc64.txt xarcade_bmc64.bin 2 | # scwr xarcade_bmc64.bin 3 | 4 | # important!: 5 | force set2ext 6 | 7 | # dir1 keypad 8 (75),2 (72),4 (6B),6 (74) 8 | # sel1 3 9 | # start1 1 10 | # A,B,C: left shift (12),Z,X 11 | # X,Y,Z: left ctrl (14),left alt (11),space (29) 12 | # lowerbuttons: c,5 13 | 14 | remapblock 15 | # left controller 16 | PAD_8 UP # stick -> up 17 | PAD_2 DOWN # stick -> down 18 | PAD_4 LEFT # stick -> left 19 | PAD_6 RIGHT # stick -> right 20 | LSHIFT LCTRL # A -> fire 21 | 22 | Z SPACE # B -> space 23 | X F7 # C -> F7 24 | LCTRL F1 # X -> F1 25 | LALT F3 # Y -> F3 26 | SPACE F5 # Z -> F5 27 | C F12 # bottomleft -> F12 28 | 5 PAGE_UP # bottomright -> Restore 29 | 3 ESC # select -> Run-Stop 30 | 1 ENTER # start -> Return 31 | 32 | 33 | # right controller 34 | R PAD_8 # stick -> up 35 | F PAD_2 # stick -> down 36 | D PAD_4 # stick -> left 37 | G PAD_6 # stick -> right 38 | W PAD_5 # A -> fire 39 | 40 | E SPACE # B -> space 41 | LEFT_BRACE 1 # C -> 1 42 | A 2 # X -> 2 43 | S 3 # Y -> 3 44 | Q 4 # Z -> 4 45 | RIGHT_BRACE 5 # bottomleft -> 5 46 | 6 6 # bottomright -> 6 47 | 2 7 # select -> 7 48 | 4 8 # start -> 8 49 | endblock 50 | 51 | # dir2 r,f,d,g 52 | # sel2 4 53 | # start2 2 54 | # A,B,C: w,e,}(å)(54="[") 55 | # X,Y,Z: a,s,q 56 | # lowerbuttons: ;(¨)(5B="]"),6 57 | -------------------------------------------------------------------------------- /PS2_Soarer_Converter/xarcade_mister.txt: -------------------------------------------------------------------------------- 1 | 2 | # scas xarcade_mister.txt xarcade_mister.bin 3 | # scwr xarcade_mister.bin 4 | 5 | # important!: 6 | force set2ext 7 | 8 | # https://mister-devel.github.io/MkDocs_MiSTer/advanced/diy2parcade/ 9 | # /media/fat/MiSTer.ini: 10 | # jamma_vid=16C0 11 | # jamma_pid=047D 12 | # then from main menu: "Define Joystick buttons" 13 | # (P2 buttons will be automatically assigned the same as for P1) 14 | 15 | # layout I set to MiSTer "Define Joystick buttons": 16 | # X Y MENU 17 | # A B OK 18 | # L R 19 | # LEFT SIDE BUTTON = SELECT 20 | # BUTTON WITH ONE HUMAN FIGURE = START 21 | # (B works as BACK, so MiSTer menu can be used with MENU, OK and B) 22 | 23 | # X-Arcade: 24 | # Letters used in X-Arcade layout: 25 | # X Y Z 26 | # A B C 27 | # L R 28 | # Left side: 29 | # UP1 = PAD_8 (0x75) 30 | # DOWN1 = PAD_2 (0x72) 31 | # LEFT1 = PAD_4 (0x6b) 32 | # RIGHT1 = PAD_6 (0x74) 33 | # SELECT1 (left "flipper" button) = 3 (0x26) 34 | # START1 (button with one human figure) = 1 (0x16) 35 | # A1 = LSHIFT (0x12) 36 | # B1 = Z (0x1a) 37 | # C1 = X (0x22) 38 | # X1 = LCTRL (0x14) 39 | # Y1 = LALT (0x11) 40 | # Z1 = SPACE (0x29) 41 | # L1 = C (0x21) 42 | # R1 = 5 (0x2e) 43 | # Right side: 44 | # UP2 = R (0x2d) 45 | # DOWN2 = F (0x2b) 46 | # LEFT2 = D (0x23) 47 | # RIGHT2 = G (0x34) 48 | # SELECT2 = 4 (0x25) 49 | # START2 = 2 (0x1e) 50 | # A2 = W (0x1d) 51 | # B2 = E (0x24) 52 | # C2 = LEFT_BRACE (0x54) 53 | # X2 = A (0x1c) 54 | # Y2 = S (0x1b) 55 | # Z2 = Q (0x15) 56 | # L2 = RIGHT_BRACE (0x5b) 57 | # R2 = 6 (0x36) 58 | 59 | # not enough buttons to support these: 60 | # 9, Test 61 | # TAB, Tab (shift + 1P right) 62 | # ENTER, Enter (shift + 1P left) 63 | # P, P (pause) (shift + 1P down) 64 | # F1, Service 65 | # F2, Test 66 | # F3, Tilt 67 | 68 | remapblock 69 | # left controller 70 | 3 5 # SELECT1 -> 1P coin 71 | #1 1 # START1 -> 1P start (shift key) 72 | PAD_8 UP # UP1 -> 1P up 73 | PAD_2 DOWN # DOWN1 -> 1P down 74 | PAD_4 LEFT # LEFT1 -> 1P left 75 | PAD_6 RIGHT # RIGHT1 -> 1P right 76 | LSHIFT LCTRL # A1 -> 1P button 1 77 | Z LALT # B1 -> 1P button 2 78 | X SPACE # C1 -> 1P button 3 79 | LCTRL LSHIFT # X1 -> 1P button 4 80 | LALT Z # Y1 -> 1P button 5 81 | SPACE X # Z1 -> 1P button 6 82 | C C # L1 -> 1P button 7 83 | 5 V # R1 -> 1P button 8 84 | 85 | # right controller 86 | 4 6 # SELECT2 -> 2P coin 87 | #2 2 # START2 -> 2P start (shift key) 88 | #R R # UP2 -> 2P up 89 | #F F # DOWN2 -> 2P down 90 | #D D # LEFT2 -> 2P left 91 | #G G # RIGHT2 -> 2P right 92 | W A # A2 -> 2P button 1 93 | E S # B2 -> 2P button 2 94 | LEFT_BRACE Q # C2 -> 2P button 3 95 | A W # X2 -> 2P button 4 96 | S I # Y2 -> 2P button 5 97 | Q K # Z2 -> 2P button 6 98 | RIGHT_BRACE J # L2 -> 2P button 7 99 | 6 L # R2 -> 2P button 8 100 | endblock 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /RetroJoystickAdapter_Atari.ino: -------------------------------------------------------------------------------- 1 | //DB9-connector: 2 | //C64/Sega Mastersystem: 1 = up, 2 = down, 3 = left, 4 = right, 6 = btn1, 8 = gnd, 9 = btn2 3 | //MSX: 1 = up, 2 = down, 3 = left, 4 = right, 6 = btn1, 7 = btn2, 8 = gnd 4 | 5 | // define pins of Arduino: UP, DOWN, LEFT, RIGHT, BTN1, BTN2 6 | const uint8_t inputPinsPort1[] = { 5, 6, 7, 8, 4, A2}; 7 | const uint8_t inputPinsPort2[] = { 10, 16, 14, 15, 3, A1}; 8 | 9 | //#define DEBUG 10 | 11 | inline void translateState(uint8_t data, uint8_t *state) { 12 | state[0] = !bitRead(data, 4) | !bitRead(data, 5)<<1; 13 | state[1] = 127; 14 | state[2] = 127; 15 | if (!bitRead(data, 0)) state[2] = 0; /* up */ 16 | if (!bitRead(data, 1)) state[2] = 255; /* down */ 17 | if (!bitRead(data, 2)) state[1] = 0; /* left */ 18 | if (!bitRead(data, 3)) state[1] = 255; /* right */ 19 | } 20 | 21 | #include "HID.h" 22 | 23 | #define JOYSTICK_REPORT_ID 0x04 24 | #define JOYSTICK2_REPORT_ID 0x05 25 | 26 | #define JOYSTICK_STATE_SIZE 3 27 | 28 | 29 | //================================================================================ 30 | //================================================================================ 31 | // Joystick (Gamepad) 32 | 33 | 34 | #define HIDDESC_MACRO(REPORT_ID) \ 35 | /* Joystick # */ \ 36 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 37 | 0x09, 0x04, /* USAGE (Joystick) */ \ 38 | 0xa1, 0x01, /* COLLECTION (Application) */ \ 39 | 0x85, REPORT_ID, /* REPORT_ID */ \ 40 | /* 2 Buttons */ \ 41 | 0x05, 0x09, /* USAGE_PAGE (Button) */ \ 42 | 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ \ 43 | 0x29, 0x02, /* USAGE_MAXIMUM (Button 2) */ \ 44 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 45 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \ 46 | 0x75, 0x01, /* REPORT_SIZE (1) */ \ 47 | 0x95, 0x08, /* REPORT_COUNT (8) (full byte) */ \ 48 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 49 | /* X and Y Axis */ \ 50 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 51 | 0x09, 0x01, /* USAGE (Pointer) */ \ 52 | 0xA1, 0x00, /* COLLECTION (Physical) */ \ 53 | 0x09, 0x30, /* USAGE (x) */ \ 54 | 0x09, 0x31, /* USAGE (y) */ \ 55 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 56 | 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ \ 57 | 0x75, 0x08, /* REPORT_SIZE (8) */ \ 58 | 0x95, 0x02, /* REPORT_COUNT (2) */ \ 59 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 60 | 0xc0, /* END_COLLECTION */ \ 61 | 0xc0 /* END_COLLECTION */ 62 | 63 | 64 | static const uint8_t hidReportDescriptor[] PROGMEM = { 65 | HIDDESC_MACRO(JOYSTICK_REPORT_ID), 66 | HIDDESC_MACRO(JOYSTICK2_REPORT_ID) 67 | }; 68 | 69 | 70 | class Joystick_ { 71 | 72 | private: 73 | uint8_t joystickId; 74 | uint8_t reportId; 75 | uint8_t olddata; 76 | uint8_t state[JOYSTICK_STATE_SIZE]; 77 | uint8_t flag; 78 | 79 | public: 80 | uint8_t type; 81 | uint8_t data; 82 | 83 | Joystick_(uint8_t initJoystickId, uint8_t initReportId) { 84 | // Setup HID report structure 85 | static bool usbSetup = false; 86 | 87 | if (!usbSetup) { 88 | static HIDSubDescriptor node(hidReportDescriptor, sizeof(hidReportDescriptor)); 89 | HID().AppendDescriptor(&node); 90 | usbSetup = true; 91 | } 92 | 93 | // Initalize State 94 | joystickId = initJoystickId; 95 | reportId = initReportId; 96 | 97 | data = 0; 98 | olddata = data; 99 | translateState(data, state); 100 | sendState(1); 101 | } 102 | 103 | void updateState() { 104 | if (olddata != data) { 105 | olddata = data; 106 | translateState(data, state); 107 | flag = 1; 108 | } 109 | } 110 | 111 | void sendState(uint8_t force = 0) { 112 | if (flag || force) { 113 | // HID().SendReport(Report number, array of values in same order as HID descriptor, length) 114 | HID().SendReport(reportId, state, JOYSTICK_STATE_SIZE); 115 | flag = 0; 116 | } 117 | } 118 | 119 | }; 120 | 121 | 122 | Joystick_ Joystick[2] = 123 | { 124 | Joystick_(0, JOYSTICK_REPORT_ID), 125 | Joystick_(1, JOYSTICK2_REPORT_ID) 126 | }; 127 | 128 | //================================================================================ 129 | //================================================================================ 130 | 131 | void setup() { 132 | //set all DB9-connector input signal pins as inputs with pullups 133 | for (uint8_t i = 0; i < 6; i++) { 134 | pinMode(inputPinsPort1[i], INPUT_PULLUP); 135 | pinMode(inputPinsPort2[i], INPUT_PULLUP); 136 | } 137 | 138 | #ifdef DEBUG 139 | Serial.begin(115200); 140 | #endif 141 | 142 | } 143 | 144 | 145 | void loop() { 146 | 147 | Joystick[0].data = 0xff; 148 | Joystick[1].data = 0xff; 149 | 150 | for (uint8_t i = 0; i < 4; i++) { 151 | bitWrite(Joystick[0].data, i, digitalRead(inputPinsPort1[i])); //AXES1 152 | bitWrite(Joystick[1].data, i, digitalRead(inputPinsPort2[i])); //AXES2 153 | } 154 | 155 | bitWrite(Joystick[0].data, 4, digitalRead(inputPinsPort1[4])); //JOY1:FIRE1 156 | bitWrite(Joystick[0].data, 5, digitalRead(inputPinsPort1[5])); //JOY1:FIRE2 157 | bitWrite(Joystick[1].data, 4, digitalRead(inputPinsPort2[4])); //JOY2:FIRE1 158 | bitWrite(Joystick[1].data, 5, digitalRead(inputPinsPort2[5])); //JOY2:FIRE2 159 | 160 | 161 | #ifdef DEBUG 162 | Serial.print(" data0 j1: 0x"); Serial.print(Joystick[0].data, BIN); 163 | Serial.print(" data0 j2: 0x"); Serial.print(Joystick[1].data, BIN); 164 | Serial.println(); 165 | delay(50); 166 | Serial.flush(); 167 | #endif 168 | 169 | Joystick[0].updateState(); 170 | Joystick[1].updateState(); 171 | Joystick[0].sendState(); 172 | Joystick[1].sendState(); 173 | delayMicroseconds(500); 174 | 175 | } 176 | -------------------------------------------------------------------------------- /RetroJoystickAdapter_Megadrive.ino: -------------------------------------------------------------------------------- 1 | 2 | //https://www.cs.cmu.edu/~chuck/infopg/segasix.txt 3 | 4 | //DB9 (8=GND, 5=VCC): 1 2 3 4 5 6 7 8 9 5 | const uint8_t inputPinsPort1[] = { 2, 3, 4, 5, 6, 7, 8, 0, 9}; 6 | const uint8_t inputPinsPort2[] = {10, 16, 14, 15, A0, A1, A2, 0, A3}; 7 | 8 | // if you use two DB9 connectors solded back to back on your ATmega32u4, you should use this inputs 9 | //const uint8_t inputPinsPort1[] = { 2, 3, 4, 5, 6, 7, 8, 0, 9}; 10 | //const uint8_t inputPinsPort2[] = {15, A0, A1, A2, A3, 14, 16, 0, 10}; 11 | 12 | // yet another version (Images/sega_genesis_adapter.jpg) 13 | //const uint8_t inputPinsPort1[] = { 5, 6, 7, 8, 9, 4, 2, 0, A2}; 14 | //const uint8_t inputPinsPort2[] = {10, 16, 14, 15, A0, 3, A3, 0, A1}; 15 | 16 | 17 | //#define DEBUG 18 | 19 | inline void translateState(uint8_t *data, uint8_t *state) { 20 | state[0] = ~data[0]; 21 | state[1] = 127; 22 | state[2] = 127; 23 | if (!bitRead(data[1], 0)) state[2] = 0; /* up */ 24 | if (!bitRead(data[1], 1)) state[2] = 255; /* down */ 25 | if (!bitRead(data[1], 2)) state[1] = 0; /* left */ 26 | if (!bitRead(data[1], 3)) state[1] = 255; /* right */ 27 | } 28 | 29 | uint8_t J1BTN6 = 0; 30 | uint8_t J2BTN6 = 0; 31 | 32 | uint8_t plugged1 = 0; 33 | uint8_t plugged2 = 0; 34 | 35 | #include "HID.h" 36 | 37 | #if ARDUINO < 10606 38 | #error The Joystick2 library requires Arduino IDE 1.6.6 or greater. Please update your IDE. 39 | #endif 40 | 41 | #if !defined(USBCON) 42 | #error The Joystick2 library can only be used with a USB MCU (e.g. Arduino Leonardo, Arduino Micro, etc.). 43 | #endif 44 | 45 | #if !defined(_USING_HID) 46 | #error "legacy HID core (non pluggable)" 47 | #endif 48 | 49 | #define JOYSTICK_REPORT_ID 0x04 50 | #define JOYSTICK2_REPORT_ID 0x05 51 | 52 | #define JOYSTICK_DATA_SIZE 2 53 | #define JOYSTICK_STATE_SIZE 3 54 | 55 | 56 | //================================================================================ 57 | //================================================================================ 58 | // Joystick (Gamepad) 59 | 60 | 61 | #define HIDDESC_MACRO(REPORT_ID) \ 62 | /* Joystick # */ \ 63 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 64 | 0x09, 0x04, /* USAGE (Joystick) */ \ 65 | 0xa1, 0x01, /* COLLECTION (Application) */ \ 66 | 0x85, REPORT_ID, /* REPORT_ID */ \ 67 | /* 8 Buttons */ \ 68 | 0x05, 0x09, /* USAGE_PAGE (Button) */ \ 69 | 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ \ 70 | 0x29, 0x08, /* USAGE_MAXIMUM (Button 8) */ \ 71 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 72 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \ 73 | 0x75, 0x01, /* REPORT_SIZE (1) */ \ 74 | 0x95, 0x08, /* REPORT_COUNT (8) */ \ 75 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 76 | /* X and Y Axis */ \ 77 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 78 | 0x09, 0x01, /* USAGE (Pointer) */ \ 79 | 0xA1, 0x00, /* COLLECTION (Physical) */ \ 80 | 0x09, 0x30, /* USAGE (x) */ \ 81 | 0x09, 0x31, /* USAGE (y) */ \ 82 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 83 | 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ \ 84 | 0x75, 0x08, /* REPORT_SIZE (8) */ \ 85 | 0x95, 0x02, /* REPORT_COUNT (2) */ \ 86 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 87 | 0xc0, /* END_COLLECTION */ \ 88 | 0xc0 /* END_COLLECTION */ 89 | 90 | 91 | 92 | 93 | static const uint8_t hidReportDescriptor[] PROGMEM = { 94 | HIDDESC_MACRO(JOYSTICK_REPORT_ID), 95 | HIDDESC_MACRO(JOYSTICK2_REPORT_ID) 96 | }; 97 | 98 | 99 | class Joystick_ { 100 | 101 | private: 102 | uint8_t joystickId; 103 | uint8_t reportId; 104 | uint8_t olddata[JOYSTICK_DATA_SIZE]; 105 | uint8_t state[JOYSTICK_STATE_SIZE]; 106 | uint8_t flag; 107 | 108 | public: 109 | uint8_t type; 110 | uint8_t data[JOYSTICK_DATA_SIZE]; 111 | 112 | Joystick_(uint8_t initJoystickId, uint8_t initReportId) { 113 | // Setup HID report structure 114 | static bool usbSetup = false; 115 | 116 | if (!usbSetup) { 117 | static HIDSubDescriptor node(hidReportDescriptor, sizeof(hidReportDescriptor)); 118 | HID().AppendDescriptor(&node); 119 | usbSetup = true; 120 | } 121 | 122 | // Initalize State 123 | joystickId = initJoystickId; 124 | reportId = initReportId; 125 | 126 | data[0] = 0; 127 | data[1] = 0; 128 | memcpy(olddata, data, JOYSTICK_DATA_SIZE); 129 | translateState(data, state); 130 | sendState(1); 131 | } 132 | 133 | void updateState() { 134 | if (memcmp(olddata, data, JOYSTICK_DATA_SIZE)) { 135 | memcpy(olddata, data, JOYSTICK_DATA_SIZE); 136 | translateState(data, state); 137 | flag = 1; 138 | } 139 | } 140 | 141 | void sendState(uint8_t force = 0) { 142 | if (flag || force) { 143 | // HID().SendReport(Report number, array of values in same order as HID descriptor, length) 144 | HID().SendReport(reportId, state, JOYSTICK_STATE_SIZE); 145 | flag = 0; 146 | } 147 | } 148 | 149 | }; 150 | 151 | 152 | Joystick_ Joystick[2] = 153 | { 154 | Joystick_(0, JOYSTICK_REPORT_ID), 155 | Joystick_(1, JOYSTICK2_REPORT_ID) 156 | }; 157 | 158 | //================================================================================ 159 | //================================================================================ 160 | 161 | #define MODE_SELECT_PORT1 inputPinsPort1[6] 162 | #define MODE_SELECT_PORT2 inputPinsPort2[6] 163 | #define VCC_PORT1 inputPinsPort1[4] 164 | #define VCC_PORT2 inputPinsPort2[4] 165 | 166 | void modeSelect(uint8_t m) { 167 | digitalWrite(MODE_SELECT_PORT1, m); 168 | digitalWrite(MODE_SELECT_PORT2, m); 169 | delayMicroseconds(20); 170 | } 171 | 172 | 173 | void setup() { 174 | for (uint8_t i = 0; i < 9; i++) { 175 | if (inputPinsPort1[i] != 0 && i != 4 && i != 6) 176 | pinMode(inputPinsPort1[i], INPUT_PULLUP); 177 | if (inputPinsPort2[i] != 0 && i != 4 && i != 6) 178 | pinMode(inputPinsPort2[i], INPUT_PULLUP); 179 | } //without PULLUP every button are read as pressed down if controller is not connected. 180 | 181 | pinMode(VCC_PORT1, OUTPUT); 182 | pinMode(VCC_PORT2, OUTPUT); 183 | digitalWrite(VCC_PORT1, HIGH); 184 | digitalWrite(VCC_PORT2, HIGH); 185 | 186 | pinMode(MODE_SELECT_PORT1, OUTPUT); 187 | pinMode(MODE_SELECT_PORT2, OUTPUT); 188 | modeSelect(HIGH); 189 | 190 | #ifdef DEBUG 191 | Serial.begin(9600); 192 | #endif 193 | 194 | } 195 | 196 | 197 | 198 | void loop() { 199 | 200 | Joystick[0].data[0] = 0xff; 201 | Joystick[1].data[0] = 0xff; 202 | Joystick[0].data[1] = 0xff; 203 | Joystick[1].data[1] = 0xff; 204 | 205 | modeSelect(LOW); 206 | 207 | bitWrite(Joystick[0].data[1], 6, digitalRead(inputPinsPort1[2])); //detect1 j1 208 | bitWrite(Joystick[0].data[1], 7, digitalRead(inputPinsPort1[3])); //detect2 j1 209 | bitWrite(Joystick[1].data[1], 6, digitalRead(inputPinsPort2[2])); //detect1 j2 210 | bitWrite(Joystick[1].data[1], 7, digitalRead(inputPinsPort2[3])); //detect2 j2 211 | 212 | bitWrite(Joystick[0].data[0], 0, digitalRead(inputPinsPort1[5])); //A1 213 | bitWrite(Joystick[0].data[0], 3, digitalRead(inputPinsPort1[8])); //Start1 214 | bitWrite(Joystick[1].data[0], 0, digitalRead(inputPinsPort2[5])); //A2 215 | bitWrite(Joystick[1].data[0], 3, digitalRead(inputPinsPort2[8])); //Start2 216 | 217 | modeSelect(HIGH); 218 | 219 | for (uint8_t i = 0; i < 4; i++) { 220 | bitWrite(Joystick[0].data[1], i, digitalRead(inputPinsPort1[i])); //AXES1 221 | bitWrite(Joystick[1].data[1], i, digitalRead(inputPinsPort2[i])); //AXES2 222 | } 223 | 224 | bitWrite(Joystick[0].data[0], 1, digitalRead(inputPinsPort1[5])); //B1 225 | bitWrite(Joystick[0].data[0], 2, digitalRead(inputPinsPort1[8])); //C1 226 | bitWrite(Joystick[1].data[0], 1, digitalRead(inputPinsPort2[5])); //B2 227 | bitWrite(Joystick[1].data[0], 2, digitalRead(inputPinsPort2[8])); //C2 228 | 229 | 230 | //read X,Y,Z,mode 231 | modeSelect(LOW); 232 | modeSelect(HIGH); 233 | modeSelect(LOW); 234 | modeSelect(HIGH); 235 | if (J1BTN6) { 236 | bitWrite(Joystick[0].data[0], 4, digitalRead(inputPinsPort1[2])); //X1 237 | bitWrite(Joystick[0].data[0], 5, digitalRead(inputPinsPort1[1])); //Y1 238 | bitWrite(Joystick[0].data[0], 6, digitalRead(inputPinsPort1[0])); //Z1 239 | bitWrite(Joystick[0].data[0], 7, digitalRead(inputPinsPort1[3])); //mode 240 | } 241 | if (J2BTN6) { 242 | bitWrite(Joystick[1].data[0], 4, digitalRead(inputPinsPort2[2])); //X1 243 | bitWrite(Joystick[1].data[0], 5, digitalRead(inputPinsPort2[1])); //Y1 244 | bitWrite(Joystick[1].data[0], 6, digitalRead(inputPinsPort2[0])); //Z1 245 | bitWrite(Joystick[1].data[0], 7, digitalRead(inputPinsPort2[3])); //mode 246 | } 247 | 248 | 249 | //detect button mode and detect if controller is unplugged 250 | uint8_t detect1 = !(Joystick[0].data[1] & B11000000); 251 | if (!plugged1 && detect1) { 252 | plugged1 = 1; 253 | digitalWrite(VCC_PORT1, LOW); 254 | delay(100); 255 | digitalWrite(VCC_PORT1, HIGH); 256 | if (!digitalRead(inputPinsPort1[0]) && !digitalRead(inputPinsPort1[1])) J1BTN6 = 1; 257 | } 258 | if (!detect1) { 259 | plugged1 = 0; 260 | J1BTN6 = 0; 261 | } 262 | 263 | //detect button mode and detect if controller is unplugged 264 | uint8_t detect2 = !(Joystick[1].data[1] & B11000000); 265 | if (!plugged2 && detect2) { 266 | plugged2 = 1; 267 | digitalWrite(VCC_PORT2, LOW); 268 | delay(100); 269 | digitalWrite(VCC_PORT2, HIGH); 270 | if (!digitalRead(inputPinsPort2[0]) && !digitalRead(inputPinsPort2[1])) J2BTN6 = 1; 271 | } 272 | if (!detect2) { 273 | plugged2 = 0; 274 | J2BTN6 = 0; 275 | } 276 | 277 | #ifdef DEBUG 278 | Serial.print(" data0 j1: 0x"); Serial.print(Joystick[0].data[0], HEX); 279 | Serial.print(" data0 j2: 0x"); Serial.print(Joystick[1].data[0], HEX); 280 | Serial.print(" data1 j1: 0x"); Serial.print(Joystick[0].data[1], HEX); 281 | Serial.print(" data1 j2: 0x"); Serial.print(Joystick[1].data[1], HEX); 282 | Serial.print(" 6btn j1: 0x"); Serial.print(J1BTN6, HEX); 283 | Serial.print(" 6btn j2: 0x"); Serial.print(J2BTN6, HEX); 284 | Serial.println(); 285 | delay(50); 286 | Serial.flush(); 287 | #endif 288 | 289 | Joystick[0].updateState(); 290 | Joystick[1].updateState(); 291 | Joystick[0].sendState(); 292 | Joystick[1].sendState(); 293 | delayMicroseconds(1000); 294 | 295 | 296 | } 297 | -------------------------------------------------------------------------------- /RetroJoystickAdapter_Playstation.ino: -------------------------------------------------------------------------------- 1 | 2 | // 5V (red) 3 | // GND (black) 4 | #define DATA1 2 // (brown) 5 | #define CMD1 3 // (orange) 6 | #define ATT1 4 // (yellow) 7 | #define CLK1 5 // (blue) 8 | 9 | /*#define DATA2 6 10 | #define CMD2 7 11 | #define ATT2 8 12 | #define CLK2 9 */ 13 | 14 | 15 | #include "HID.h" 16 | 17 | #if ARDUINO < 10606 18 | #error The Joystick2 library requires Arduino IDE 1.6.6 or greater. Please update your IDE. 19 | #endif 20 | 21 | #if !defined(USBCON) 22 | #error The Joystick2 library can only be used with a USB MCU (e.g. Arduino Leonardo, Arduino Micro, etc.). 23 | #endif 24 | 25 | #if !defined(_USING_HID) 26 | #error "legacy HID core (non pluggable)" 27 | #endif 28 | 29 | #define JOYSTICK_REPORT_ID 0x03 30 | #define JOYSTICK2_REPORT_ID 0x04 31 | #define JOYSTICK3_REPORT_ID 0x05 32 | #define JOYSTICK4_REPORT_ID 0x06 33 | 34 | #define JOYSTICK_STATE_SIZE 6 35 | 36 | //#define DEBUG 37 | 38 | //================================================================================ 39 | //================================================================================ 40 | // Joystick (Gamepad) 41 | 42 | 43 | #define HIDDESC_MACRO(REPORT_ID) \ 44 | /* Joystick # */ \ 45 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 46 | 0x09, 0x04, /* USAGE (Joystick) */ \ 47 | 0xa1, 0x01, /* COLLECTION (Application) */ \ 48 | 0x85, REPORT_ID, /* REPORT_ID */ \ 49 | /* 16 Buttons */ \ 50 | 0x05, 0x09, /* USAGE_PAGE (Button) */ \ 51 | 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ \ 52 | 0x29, 0x10, /* USAGE_MAXIMUM (Button 16) */ \ 53 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 54 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \ 55 | 0x75, 0x01, /* REPORT_SIZE (1) */ \ 56 | 0x95, 0x10, /* REPORT_COUNT (16) */ \ 57 | 0x55, 0x00, /* UNIT_EXPONENT (0) */ \ 58 | 0x65, 0x00, /* UNIT (None) */ \ 59 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 60 | /* X and Y Axis */ \ 61 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 62 | 0x09, 0x01, /* USAGE (Pointer) */ \ 63 | 0xA1, 0x00, /* COLLECTION (Physical) */ \ 64 | 0x09, 0x32, /* USAGE (Z) */ \ 65 | 0x09, 0x35, /* USAGE (Rz) */ \ 66 | 0x09, 0x30, /* USAGE (x) */ \ 67 | 0x09, 0x31, /* USAGE (y) */ \ 68 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 69 | 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ \ 70 | 0x75, 0x08, /* REPORT_SIZE (8) */ \ 71 | 0x95, 0x04, /* REPORT_COUNT (4) */ \ 72 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 73 | 0xc0, /* END_COLLECTION */ \ 74 | 0xc0 /* END_COLLECTION */ 75 | 76 | 77 | 78 | 79 | static const uint8_t hidReportDescriptor[] PROGMEM = { 80 | HIDDESC_MACRO(JOYSTICK_REPORT_ID), 81 | HIDDESC_MACRO(JOYSTICK2_REPORT_ID), 82 | HIDDESC_MACRO(JOYSTICK3_REPORT_ID), 83 | HIDDESC_MACRO(JOYSTICK4_REPORT_ID) 84 | }; 85 | 86 | 87 | class Joystick_ { 88 | 89 | private: 90 | uint8_t joystickId; 91 | uint8_t reportId; 92 | uint8_t olddata[JOYSTICK_STATE_SIZE]; 93 | uint8_t flag; 94 | 95 | public: 96 | uint8_t type; 97 | uint8_t data[JOYSTICK_STATE_SIZE]; 98 | 99 | Joystick_(uint8_t initJoystickId, uint8_t initReportId) { 100 | // Setup HID report structure 101 | static bool usbSetup = false; 102 | 103 | if (!usbSetup) { 104 | static HIDSubDescriptor node(hidReportDescriptor, sizeof(hidReportDescriptor)); 105 | HID().AppendDescriptor(&node); 106 | usbSetup = true; 107 | } 108 | 109 | // Initalize State 110 | joystickId = initJoystickId; 111 | reportId = initReportId; 112 | 113 | data[0] = 0; 114 | data[1] = 0; 115 | data[2] = 127; 116 | data[3] = 127; 117 | data[4] = 127; 118 | data[5] = 127; 119 | memcpy(olddata, data, JOYSTICK_STATE_SIZE); 120 | sendState(1); 121 | } 122 | 123 | void updateState() { 124 | if (type != 0x73 && type != 0x53) { 125 | data[2] = 127; 126 | data[3] = 127; 127 | data[4] = 127; 128 | data[5] = 127; 129 | } 130 | if (type == 0x41 || type == 0x73 || type == 0x53) { 131 | if (memcmp(olddata, data, JOYSTICK_STATE_SIZE)) { 132 | memcpy(olddata, data, JOYSTICK_STATE_SIZE); 133 | flag = 1; 134 | } 135 | } 136 | //sendState(); 137 | } 138 | 139 | void sendState(uint8_t force = 0) { 140 | if (flag || force) { 141 | // HID().SendReport(Report number, array of values in same order as HID descriptor, length) 142 | HID().SendReport(reportId, data, JOYSTICK_STATE_SIZE); 143 | flag = 0; 144 | } 145 | } 146 | 147 | }; 148 | 149 | 150 | Joystick_ Joystick[4] = 151 | { 152 | Joystick_(0, JOYSTICK_REPORT_ID), 153 | Joystick_(1, JOYSTICK2_REPORT_ID), 154 | Joystick_(2, JOYSTICK3_REPORT_ID), 155 | Joystick_(3, JOYSTICK4_REPORT_ID) 156 | }; 157 | 158 | //================================================================================ 159 | //================================================================================ 160 | 161 | 162 | 163 | 164 | 165 | uint8_t shift(uint8_t _dataOut) // Does the actual shifting, both in and out simultaneously 166 | { 167 | uint8_t _temp = 0; 168 | uint8_t _dataIn = 0; 169 | uint8_t _delay = 6; //2 unstable; //clock 250kHz 170 | 171 | delayMicroseconds(100); //max acknowledge waiting time 100us 172 | for (uint8_t _i = 0; _i <= 7; _i++) { 173 | 174 | if ( _dataOut & (1 << _i) ) // write bit 175 | digitalWrite(CMD1, HIGH); 176 | else 177 | digitalWrite(CMD1, LOW); 178 | 179 | digitalWrite(CLK1, LOW); // read bit 180 | delayMicroseconds(_delay); 181 | _temp = digitalRead(DATA1); 182 | if (_temp) { 183 | _dataIn = _dataIn | (B00000001 << _i); 184 | } 185 | 186 | digitalWrite(CLK1, HIGH); 187 | delayMicroseconds(_delay); 188 | } 189 | return _dataIn; 190 | } 191 | 192 | void setup() { 193 | pinMode(DATA1, INPUT_PULLUP); 194 | pinMode(CMD1, OUTPUT); 195 | pinMode(ATT1, OUTPUT); 196 | pinMode(CLK1, OUTPUT); 197 | 198 | /*pinMode(DATA2, INPUT_PULLUP); 199 | pinMode(CMD2, OUTPUT); 200 | pinMode(ATT2, OUTPUT); 201 | pinMode(CLK2, OUTPUT);*/ 202 | 203 | #ifdef DEBUG 204 | Serial.begin(115200); 205 | #endif 206 | 207 | } 208 | 209 | void loop() { 210 | // http://problemkaputt.de/psx-spx.htm#controllerandmemorycardsignals 211 | uint8_t head, padding, multitap; 212 | #ifdef DEBUG 213 | uint8_t data[100]; 214 | #endif 215 | 216 | // first: read gamepad normally 217 | digitalWrite(ATT1, LOW); 218 | //digitalWrite(ATT2, LOW); 219 | head = shift(0x01); 220 | Joystick[0].type = shift(0x42); 221 | padding = shift(0x01); //read multitap in next command 222 | Joystick[0].data[0] = ~shift(0x00); //buttons 223 | Joystick[0].data[1] = ~shift(0x00); //buttons 224 | Joystick[0].data[2] = shift(0x00); //right analog 225 | Joystick[0].data[3] = shift(0x00); //right analog 226 | Joystick[0].data[4] = shift(0x00); //left analog 227 | Joystick[0].data[5] = shift(0x00); //left analog 228 | digitalWrite(ATT1, HIGH); 229 | //digitalWrite(ATT2, HIGH); 230 | 231 | //delay(100); 232 | 233 | // second: check and read multitap 234 | digitalWrite(ATT1, LOW); 235 | head = shift(0x01); 236 | multitap = shift(0x42); 237 | padding = shift(0x00); //next time normal read 238 | if (multitap == 0x80) { 239 | for (uint8_t i = 0; i < 4; i++) { 240 | Joystick[i].type = shift(0x00); 241 | padding = shift(0x00); 242 | Joystick[i].data[0] = ~shift(0x00); //buttons 243 | Joystick[i].data[1] = ~shift(0x00); //buttons 244 | Joystick[i].data[2] = shift(0x00); //right analog 245 | Joystick[i].data[3] = shift(0x00); //right analog 246 | Joystick[i].data[4] = shift(0x00); //left analog 247 | Joystick[i].data[5] = shift(0x00); //left analog 248 | } 249 | } 250 | digitalWrite(ATT1, HIGH); 251 | 252 | #ifdef DEBUG 253 | for (uint8_t i = 0; i < 4; i++) { 254 | Serial.print(" multitap: "); Serial.println(multitap, HEX); 255 | Serial.print(" type: 0x"); Serial.print(Joystick[i].type, HEX); 256 | Serial.print(" data: 0x"); Serial.print(Joystick[i].data[0], HEX); 257 | Serial.print(" 0x"); Serial.print(Joystick[i].data[1], HEX); 258 | Serial.print(" 0x"); Serial.print(Joystick[i].data[2], HEX); 259 | Serial.print(" 0x"); Serial.print(Joystick[i].data[3], HEX); 260 | Serial.print(" 0x"); Serial.print(Joystick[i].data[4], HEX); 261 | Serial.print(" 0x"); Serial.print(Joystick[i].data[5], HEX); 262 | Serial.println(); 263 | } 264 | /*Serial.print(" type: 0x"); Serial.print(Joystick[0].type, HEX); 265 | Serial.print(" data: 0x"); Serial.print(Joystick[0].data[0], HEX); 266 | Serial.print(" 0x"); Serial.print(Joystick[0].data[1], HEX); 267 | Serial.print(" 0x"); Serial.print(Joystick[0].data[2], HEX); 268 | Serial.print(" 0x"); Serial.print(Joystick[0].data[3], HEX); 269 | Serial.print(" 0x"); Serial.print(Joystick[0].data[4], HEX); 270 | Serial.print(" 0x"); Serial.print(Joystick[0].data[5], HEX); 271 | Serial.println();*/ 272 | Serial.flush(); 273 | #endif 274 | 275 | Joystick[0].updateState(); 276 | Joystick[1].updateState(); 277 | Joystick[2].updateState(); 278 | Joystick[3].updateState(); 279 | Joystick[0].sendState(); 280 | Joystick[1].sendState(); 281 | Joystick[2].sendState(); 282 | Joystick[3].sendState(); 283 | delayMicroseconds(1000); 284 | 285 | 286 | } 287 | -------------------------------------------------------------------------------- /RetroJoystickAdapter_WiiExtension.ino: -------------------------------------------------------------------------------- 1 | //Uno: SDA = A4, SCL = A5 2 | //Pro Micro: SDA = D2, SCL = D3 3 | 4 | //================================================================================ 5 | //================================================================================ 6 | // Joystick (Gamepad) 7 | 8 | #include "HID.h" 9 | 10 | #if ARDUINO < 10606 11 | #error The Joystick2 library requires Arduino IDE 1.6.6 or greater. Please update your IDE. 12 | #endif 13 | 14 | #if !defined(USBCON) 15 | #error The Joystick2 library can only be used with a USB MCU (e.g. Arduino Leonardo, Arduino Micro, etc.). 16 | #endif 17 | 18 | #if !defined(_USING_HID) 19 | #error "Using legacy HID core (non pluggable)" 20 | #endif 21 | 22 | #define JOYSTICK_REPORT_ID 0x04 23 | #define JOYSTICK_STATE_SIZE 7 24 | #define JOYSTICK_DATA_SIZE 6 25 | 26 | #define NUNCHUCK 1 27 | #define CLASSIC_CONTROLLER 2 28 | #define CLASSIC_CONTROLLER_PRO 3 29 | 30 | //#define DEBUG 31 | 32 | #define HIDDESC_MACRO(REPORT_ID) \ 33 | /* Joystick # */ \ 34 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 35 | 0x09, 0x04, /* USAGE (Joystick) */ \ 36 | 0xa1, 0x01, /* COLLECTION (Application) */ \ 37 | 0x85, REPORT_ID, /* REPORT_ID */ \ 38 | /* 15 Buttons */ \ 39 | 0x05, 0x09, /* USAGE_PAGE (Button) */ \ 40 | 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ \ 41 | 0x29, 0x0F, /* USAGE_MAXIMUM (Button 15) */ \ 42 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 43 | 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ \ 44 | 0x75, 0x01, /* REPORT_SIZE (1) */ \ 45 | 0x95, 0x0F, /* REPORT_COUNT (15) */ \ 46 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 47 | 0x75, 0x01, /* REPORT_SIZE (1) */ \ 48 | 0x95, 0x01, /* REPORT_COUNT (1) */ \ 49 | 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ \ 50 | /* X and Y Axis */ \ 51 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ \ 52 | 0x09, 0x01, /* USAGE (Pointer) */ \ 53 | 0xA1, 0x00, /* COLLECTION (Physical) */ \ 54 | 0x09, 0x30, /* USAGE (x) */ \ 55 | 0x09, 0x31, /* USAGE (y) */ \ 56 | 0x09, 0x33, /* USAGE (Rx) */ \ 57 | 0x09, 0x34, /* USAGE (Ry) */ \ 58 | 0x09, 0x35, /* USAGE (Rz) */ \ 59 | 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ \ 60 | 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ \ 61 | 0x75, 0x08, /* REPORT_SIZE (8) */ \ 62 | 0x95, 0x05, /* REPORT_COUNT (5) */ \ 63 | 0x81, 0x02, /* INPUT (Data,Var,Abs) */ \ 64 | 0xc0, /* END_COLLECTION */ \ 65 | 0xc0 /* END_COLLECTION */ 66 | 67 | 68 | static const uint8_t hidReportDescriptor[] PROGMEM = { 69 | HIDDESC_MACRO(JOYSTICK_REPORT_ID) 70 | }; 71 | 72 | class Joystick_ { 73 | 74 | private: 75 | uint8_t joystickId; 76 | uint8_t reportId; 77 | uint8_t olddata[JOYSTICK_DATA_SIZE]; 78 | uint8_t state[JOYSTICK_STATE_SIZE]; 79 | uint8_t flag; 80 | 81 | public: 82 | uint8_t data[JOYSTICK_DATA_SIZE]; 83 | 84 | Joystick_(uint8_t initJoystickId, uint8_t initReportId) { 85 | // Setup HID report structure 86 | static bool usbSetup = false; 87 | 88 | if (!usbSetup) { 89 | static HIDSubDescriptor node(hidReportDescriptor, sizeof(hidReportDescriptor)); 90 | HID().AppendDescriptor(&node); 91 | usbSetup = true; 92 | } 93 | 94 | // Initalize State 95 | joystickId = initJoystickId; 96 | reportId = initReportId; 97 | memcpy(olddata, data, JOYSTICK_DATA_SIZE); 98 | state[0] = 0; 99 | state[1] = 0; 100 | state[2] = 127; 101 | state[3] = 127; 102 | state[4] = 127; 103 | state[5] = 127; 104 | state[6] = 127; 105 | sendState(1); 106 | } 107 | 108 | void updateState(uint8_t type) { 109 | if (memcmp(olddata, data, JOYSTICK_DATA_SIZE)) { 110 | memcpy(olddata, data, JOYSTICK_DATA_SIZE); 111 | flag = 1; 112 | } 113 | 114 | if (type == NUNCHUCK) { 115 | // http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck 116 | uint8_t SX = data[0]; 117 | uint8_t SY = data[1]; 118 | uint16_t AX = (data[2] << 2) | (data[5] >> 2 & B11); 119 | uint16_t AY = (data[3] << 2) | (data[5] >> 4 & B11); 120 | uint16_t AZ = (data[4] << 2) | (data[5] >> 6 & B11); 121 | uint8_t BC = (~data[5] >> 1 & 1); 122 | uint8_t BZ = (~data[5] & 1); 123 | 124 | state[0] = (BZ << 1) | BC; 125 | state[1] = 0; 126 | state[2] = SX; 127 | state[3] = ~SY; 128 | state[4] = AX >> 2; 129 | state[5] = AY >> 2; 130 | state[6] = AZ >> 2; 131 | } 132 | if (type == CLASSIC_CONTROLLER || type == CLASSIC_CONTROLLER_PRO) { 133 | // http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller 134 | uint8_t LX = (data[0] & 0x3f); //6bit 135 | uint8_t LY = (data[1] & 0x3f); //6bit 136 | uint8_t RX = ((data[0] & 0xc0) >> 3) + ((data[1] & 0xc0) >> 5) + ((data[2] & 0x80) >> 7); //5bit 137 | uint8_t RY = (data[2] & 0x1f); //5bit 138 | uint8_t BDU = ~data[5] & 1; 139 | uint8_t BDD = ~data[4] >> 6 & 1; 140 | uint8_t BDL = ~data[5] >> 1 & 1; 141 | uint8_t BDR = ~data[4] >> 7 & 1; 142 | uint8_t Bselect = ~data[4] >> 4 & 1; 143 | uint8_t BH = ~data[4] >> 3 & 1; 144 | uint8_t Bstart = ~data[4] >> 2 & 1; 145 | uint8_t BA = ~data[5] >> 4 & 1; 146 | uint8_t BB = ~data[5] >> 6 & 1; 147 | uint8_t BX = ~data[5] >> 3 & 1; 148 | uint8_t BY = ~data[5] >> 5 & 1; 149 | uint8_t BLT = ~data[4] >> 5 & 1; 150 | uint8_t BRT = ~data[4] >> 1 & 1; 151 | uint8_t BZL = ~data[5] >> 7 & 1; 152 | uint8_t BZR = ~data[5] >> 2 & 1; 153 | uint8_t LT = ((data[2] & 0x60) >> 2) + ((data[3] & 0xe0) >> 5); 154 | uint8_t RT = (data[3] & 0x1f); 155 | 156 | state[0] = (BZR << 7) | (BZL << 6) | (BRT << 5) | (BLT << 4) | (BY << 3) | (BX << 2) | (BB << 1) | BA; 157 | state[1] = (Bstart << 6) | (BH << 5) | (Bselect << 4) | (BDR << 3) | (BDL << 2) | (BDD << 1) | BDU; 158 | state[2] = LX << 2; 159 | state[3] = ~(LY << 2); 160 | state[4] = RX << 3; 161 | state[5] = ~(RY << 3); 162 | state[6] = 127; 163 | } 164 | } 165 | 166 | void sendState(uint8_t force = 0) { 167 | if (flag || force) { 168 | // HID().SendReport(Report number, array of values in same order as HID descriptor, length) 169 | HID().SendReport(reportId, state, JOYSTICK_STATE_SIZE); 170 | flag = 0; 171 | } 172 | } 173 | 174 | }; 175 | 176 | 177 | Joystick_ Joystick[1] = 178 | { 179 | Joystick_(0, JOYSTICK_REPORT_ID) 180 | }; 181 | 182 | //================================================================================ 183 | //================================================================================ 184 | 185 | #include 186 | 187 | #define ADDRESS 0x52 188 | 189 | uint16_t type; 190 | 191 | const uint8_t ident_nunchuck[] = { 0x00, 0x00, 0xA4, 0x20, 0x00, 0x00 }; 192 | const uint8_t ident_classic_controller[] = { 0x00, 0x00, 0xA4, 0x20, 0x01, 0x01 }; 193 | const uint8_t ident_classic_controller_pro[] = { 0x01, 0x00, 0xA4, 0x20, 0x00, 0x00 }; 194 | 195 | 196 | void sendByte(uint8_t data, uint8_t location) { 197 | Wire.beginTransmission(ADDRESS); 198 | Wire.write(location); 199 | Wire.write(data); 200 | Wire.endTransmission(); 201 | } 202 | 203 | void sendByte(uint8_t data) { 204 | Wire.beginTransmission(ADDRESS); 205 | Wire.write(data); 206 | Wire.endTransmission(); 207 | } 208 | 209 | uint8_t initExtension() { 210 | uint8_t buf[6]; 211 | uint8_t type = CLASSIC_CONTROLLER; // default type 212 | sendByte(0x55, 0xF0); 213 | sendByte(0x00, 0xFB); 214 | sendByte(0xFA); 215 | delayMicroseconds(200); 216 | Wire.requestFrom(ADDRESS, 6); 217 | uint8_t i = 0; 218 | while(Wire.available()) { 219 | buf[i] = Wire.read(); 220 | i++; 221 | if (i >= 6) break; 222 | } 223 | if (memcmp(buf, ident_nunchuck, 6) == 0) type = NUNCHUCK; 224 | if (memcmp(buf, ident_classic_controller, 6) == 0) type = CLASSIC_CONTROLLER; 225 | if (memcmp(buf, ident_classic_controller_pro, 6) == 0) type = CLASSIC_CONTROLLER_PRO; 226 | return type; 227 | } 228 | 229 | void setup() { 230 | Wire.begin(); 231 | type = initExtension(); 232 | 233 | #ifdef DEBUG 234 | Serial.begin(115200); 235 | #endif 236 | 237 | } 238 | 239 | void loop() { 240 | 241 | #ifdef DEBUG 242 | unsigned long t = micros(); 243 | #endif 244 | 245 | sendByte(0x00); 246 | delayMicroseconds(200); 247 | Wire.requestFrom(ADDRESS, 6); //request data from wii nunchuck 248 | uint8_t i = 0; 249 | while(Wire.available()) { 250 | Joystick[0].data[i] = Wire.read(); 251 | i++; 252 | if (i >= JOYSTICK_DATA_SIZE) break; 253 | } 254 | 255 | //detect if init is needed 256 | if (i < JOYSTICK_DATA_SIZE) { 257 | delay(10); 258 | type = initExtension(); 259 | #ifdef DEBUG 260 | Serial.println("Init!"); 261 | #endif 262 | delay(10); 263 | } 264 | 265 | #ifdef DEBUG 266 | Serial.println(micros()-t); 267 | #endif 268 | 269 | #ifdef DEBUG 270 | for (uint8_t i = 0; i < JOYSTICK_DATA_SIZE; i++) { 271 | Serial.print(data[i], HEX); 272 | Serial.print(" "); 273 | } 274 | Serial.println(); 275 | #endif 276 | 277 | Joystick[0].updateState(type); 278 | Joystick[0].sendState(); 279 | #ifdef DEBUG 280 | delay(100); 281 | #endif 282 | delayMicroseconds(500); 283 | } 284 | -------------------------------------------------------------------------------- /Tutorial/JoystickBlink.ino: -------------------------------------------------------------------------------- 1 | #include "Joystick2.h" 2 | 3 | void setup() { 4 | Joystick[0].begin(true); 5 | } 6 | 7 | void loop() { 8 | Joystick[0].setButton(0, 1); 9 | delay(1000); 10 | Joystick[0].setButton(0, 0); 11 | delay(1000); 12 | } 13 | -------------------------------------------------------------------------------- /Tutorial/README.md: -------------------------------------------------------------------------------- 1 | ## Tutorial 2 | 3 | ### JoystickBlink.ino 4 | Blinks button 0 in 1 second interval. 5 | 6 | ![Windows Game Controller Properties](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/master/Images/Windows_Game_Controller_properties.jpg) 7 | 8 | ### SimpleAtariExample.ino 9 | Simple 1-joystick Atari adapter for learning purposes. 10 | 11 | Easy to modify for your own DIY-joystick (like arcade cabinet) which based on on/off-switches. 12 | 13 | ### SimpleAtariExample_keyboard.ino 14 | There is also keyboard version, which acts like USB keyboard. 15 | 16 | 17 | ### Notice 18 | For testing you don't have to have real joystick connected. You can just connect IO-pin to GND with piece of wire or something. 19 | 20 | If you program Arduino sometimes to keyboard and sometimes to joystick, COM-port might change in Windows. Just select new COM-port and upload again. 21 | -------------------------------------------------------------------------------- /Tutorial/SimpleAtariExample.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple Atari joystick adapter (joystick HID) for learning purposes 3 | * McGurk 6.9.2016 4 | */ 5 | 6 | #include "Joystick2.h" 7 | 8 | // Here we define Arduino pins we use. Along with these we have to connect joystick ground to Arduino GND. 9 | // Because of Arduino internal pullup resistor off state is 1. Moving joystick switches corresponding pin to ground and then it is 0. 10 | #define UP 2 // 9-pin D-connector pin 1 11 | #define DOWN 3 // 9-pin D-connector pin 2 12 | #define LEFT 4 // 9-pin D-connector pin 3 13 | #define RIGHT 5 // 9-pin D-connector pin 4 14 | #define BUTTON 6 // 9-pin D-connector pin 6 15 | // 9-pin D-connector pin 8 to GND 16 | 17 | 18 | // We keep record how everything was last time, so we can compare if anything have changed. (1 = off, 0 = on) 19 | byte lastUP = 1; 20 | byte lastDOWN = 1; 21 | byte lastLEFT = 1; 22 | byte lastRIGHT = 1; 23 | byte lastBUTTON = 1; 24 | 25 | byte newUP = 1; 26 | byte newDOWN = 1; 27 | byte newLEFT = 1; 28 | byte newRIGHT = 1; 29 | byte newBUTTON = 1; 30 | 31 | 32 | void setup() { 33 | 34 | pinMode(UP, INPUT_PULLUP); 35 | pinMode(DOWN, INPUT_PULLUP); 36 | pinMode(LEFT, INPUT_PULLUP); 37 | pinMode(RIGHT, INPUT_PULLUP); 38 | pinMode(BUTTON, INPUT_PULLUP); 39 | 40 | Joystick[0].begin(false); 41 | 42 | } 43 | 44 | 45 | // We use this flag to indicate if anything changes. 46 | byte flag = 0; 47 | 48 | void loop() 49 | { 50 | 51 | // Read joystick state (1 = off, 0 = on). 52 | newUP = digitalRead(UP); 53 | newDOWN = digitalRead(DOWN); 54 | newLEFT = digitalRead(LEFT); 55 | newRIGHT = digitalRead(RIGHT); 56 | newBUTTON = digitalRead(BUTTON); 57 | 58 | 59 | // Check if anything changed. This way we don't have to send anything to USB if nothing happened after last time. 60 | 61 | if (newUP != lastUP) { 62 | lastUP = newUP; 63 | flag = 1; 64 | } 65 | 66 | if (newDOWN != lastDOWN) { 67 | lastDOWN = newDOWN; 68 | flag = 1; 69 | } 70 | 71 | if (newLEFT != lastLEFT) { 72 | lastLEFT = newLEFT; 73 | flag = 1; 74 | } 75 | 76 | if (newRIGHT != lastRIGHT) { 77 | lastRIGHT = newRIGHT; 78 | flag = 1; 79 | } 80 | 81 | if (newBUTTON != lastBUTTON) { 82 | lastBUTTON = newBUTTON; 83 | flag = 1; 84 | } 85 | 86 | // If anythings changed, build and send new state. 87 | if (flag) { 88 | 89 | // Clear directions and buttons before start building new state. 90 | Joystick[0].setYAxis(0); 91 | Joystick[0].setXAxis(0); 92 | Joystick[0].setButton(0, 0); 93 | 94 | // Build new state according what we read earlier. 95 | // We need exclamation mark in these if-conditions, because swithes are active low (0 = on, 1 = off). 96 | if (!newUP) { 97 | Joystick[0].setYAxis(-127); //UP 98 | } 99 | if (!newDOWN) { 100 | Joystick[0].setYAxis(127); //DOWN 101 | } 102 | if (!newLEFT) { 103 | Joystick[0].setXAxis(-127); //LEFT 104 | } 105 | if (!newRIGHT) { 106 | Joystick[0].setXAxis(127); //RIGHT 107 | } 108 | if (!newBUTTON) { 109 | Joystick[0].setButton(0, 1); //BUTTON 110 | } 111 | 112 | // Everything is ready. Send state. Before this point, we haven't send any joystick directions to USB. 113 | Joystick[0].sendState(); 114 | 115 | // Remember to clear flag. 116 | flag = 0; 117 | 118 | } 119 | 120 | // Little 1ms breathing break. 121 | delayMicroseconds(1000); 122 | 123 | } 124 | -------------------------------------------------------------------------------- /Tutorial/SimpleAtariExample_keyboard.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple Atari joystick adapter (keyboard HID) for learning purposes 3 | * McGurk 6.9.2016 4 | */ 5 | 6 | #include ; 7 | // List of special keys: https://www.arduino.cc/en/Reference/KeyboardModifiers 8 | 9 | // Here we define Arduino pins we use. Along with these we have to connect joystick ground to Arduino GND. 10 | // Because of Arduino internal pullup resistor off state is 1. Moving joystick switches corresponding pin to ground and then it is 0. 11 | #define UP 2 // 9-pin D-connector pin 1 12 | #define DOWN 3 // 9-pin D-connector pin 2 13 | #define LEFT 4 // 9-pin D-connector pin 3 14 | #define RIGHT 5 // 9-pin D-connector pin 4 15 | #define BUTTON 6 // 9-pin D-connector pin 6 16 | // 9-pin D-connector pin 8 to GND 17 | 18 | 19 | // We keep record how everything was last time, so we can compare if anything have changed. (1 = off, 0 = on) 20 | byte lastUP = 1; 21 | byte lastDOWN = 1; 22 | byte lastLEFT = 1; 23 | byte lastRIGHT = 1; 24 | byte lastBUTTON = 1; 25 | 26 | byte newUP = 1; 27 | byte newDOWN = 1; 28 | byte newLEFT = 1; 29 | byte newRIGHT = 1; 30 | byte newBUTTON = 1; 31 | 32 | 33 | void setup() { 34 | 35 | pinMode(UP, INPUT_PULLUP); 36 | pinMode(DOWN, INPUT_PULLUP); 37 | pinMode(LEFT, INPUT_PULLUP); 38 | pinMode(RIGHT, INPUT_PULLUP); 39 | pinMode(BUTTON, INPUT_PULLUP); 40 | 41 | Keyboard.begin(); 42 | 43 | } 44 | 45 | // We use this flag to indicate if anything changes. 46 | byte flag = 0; 47 | 48 | 49 | void loop() 50 | { 51 | 52 | // Read joystick state (1 = off, 0 = on). 53 | newUP = digitalRead(UP); 54 | newDOWN = digitalRead(DOWN); 55 | newLEFT = digitalRead(LEFT); 56 | newRIGHT = digitalRead(RIGHT); 57 | newBUTTON = digitalRead(BUTTON); 58 | 59 | 60 | // Check if anything changed. This way we don't have to send anything to USB, if nothing changes. 61 | 62 | if (newUP != lastUP) { 63 | lastUP = newUP; 64 | flag = 1; 65 | } 66 | 67 | if (newDOWN != lastDOWN) { 68 | lastDOWN = newDOWN; 69 | flag = 1; 70 | } 71 | 72 | if (newLEFT != lastLEFT) { 73 | lastLEFT = newLEFT; 74 | flag = 1; 75 | } 76 | 77 | if (newRIGHT != lastRIGHT) { 78 | lastRIGHT = newRIGHT; 79 | flag = 1; 80 | } 81 | 82 | if (newBUTTON != lastBUTTON) { 83 | lastBUTTON = newBUTTON; 84 | flag = 1; 85 | } 86 | 87 | // If anythings changed, send new state. 88 | if (flag) { 89 | 90 | // Press or release keyboard button according what we read earlier. 91 | // We need exclamation mark in these if-conditions, because swithes are active low (0 = on, 1 = off). 92 | if (!newUP) { 93 | Keyboard.press('w'); 94 | } else { 95 | Keyboard.release('w'); 96 | } 97 | if (!newDOWN) { 98 | Keyboard.press('s'); 99 | } else { 100 | Keyboard.release('s'); 101 | } 102 | if (!newLEFT) { 103 | Keyboard.press('a'); 104 | } else { 105 | Keyboard.release('a'); 106 | } 107 | if (!newRIGHT) { 108 | Keyboard.press('d'); 109 | } else { 110 | Keyboard.release('d'); 111 | } 112 | if (!newBUTTON) { 113 | Keyboard.press('e'); 114 | } else { 115 | Keyboard.release('e'); 116 | } 117 | 118 | // Remember to clear flag. 119 | flag = 0; 120 | 121 | } 122 | 123 | // Little 1ms breathing break. 124 | delayMicroseconds(1000); 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Wii_Extension_debug.ino: -------------------------------------------------------------------------------- 1 | //Uno: SDA = A4, SCL = A5 2 | //Pro Micro: SDA = D2, SCL = D3 3 | //ESP8266: SDA = D2, SCL = D1 4 | 5 | 6 | #define DEBUG 7 | 8 | #include 9 | 10 | #define JOYSTICK_DATA_SIZE 6 11 | uint8_t data[JOYSTICK_DATA_SIZE]; 12 | 13 | #define ADDRESS 0x52 14 | 15 | uint16_t type; 16 | 17 | const uint8_t ident_nunchuck[] = { 0x00, 0x00, 0xA4, 0x20, 0x00, 0x00 }; 18 | const uint8_t ident_classic_controller[] = { 0x00, 0x00, 0xA4, 0x20, 0x01, 0x01 }; 19 | const uint8_t ident_classic_controller_pro[] = { 0x01, 0x00, 0xA4, 0x20, 0x00, 0x00 }; 20 | 21 | #define NUNCHUCK 1 22 | #define CLASSIC_CONTROLLER 2 23 | #define CLASSIC_CONTROLLER_PRO 3 24 | 25 | 26 | void sendByte(uint8_t data, uint8_t location) { 27 | Wire.beginTransmission(ADDRESS); 28 | Wire.write(location); 29 | Wire.write(data); 30 | Wire.endTransmission(); 31 | } 32 | 33 | void sendByte(uint8_t data) { 34 | Wire.beginTransmission(ADDRESS); 35 | Wire.write(data); 36 | Wire.endTransmission(); 37 | } 38 | 39 | uint8_t initExtension() { 40 | uint8_t buf[6]; 41 | uint8_t type = CLASSIC_CONTROLLER; // default type 42 | sendByte(0x55, 0xF0); 43 | sendByte(0x00, 0xFB); 44 | sendByte(0xFA); 45 | delayMicroseconds(200); 46 | Wire.requestFrom(ADDRESS, 6); 47 | uint8_t i = 0; 48 | while(Wire.available()) { 49 | buf[i] = Wire.read(); 50 | i++; 51 | if (i >= 6) break; 52 | } 53 | if (memcmp(buf, ident_nunchuck, 6) == 0) type = NUNCHUCK; 54 | if (memcmp(buf, ident_classic_controller, 6) == 0) type = CLASSIC_CONTROLLER; 55 | if (memcmp(buf, ident_classic_controller_pro, 6) == 0) type = CLASSIC_CONTROLLER_PRO; 56 | return type; 57 | 58 | } 59 | 60 | void setup() { 61 | Wire.begin(); 62 | //Wire.setClock(400000L); 63 | //Wire.setClock(300000L); 64 | //Wire.setClock(200000L); 65 | //Wire.setClock(100000L); 66 | type = initExtension(); 67 | 68 | Serial.begin(115200); 69 | Serial.println("start"); 70 | Serial.println(type); 71 | Serial.flush(); 72 | } 73 | 74 | 75 | void loop() { 76 | 77 | #ifdef DEBUG 78 | unsigned long t = micros(); 79 | #endif 80 | 81 | sendByte(0x00); 82 | delayMicroseconds(200); 83 | Wire.requestFrom(ADDRESS, JOYSTICK_DATA_SIZE); 84 | uint8_t i = 0; 85 | while(Wire.available()) { 86 | data[i] = Wire.read(); 87 | i++; 88 | if (i >= JOYSTICK_DATA_SIZE) break; 89 | } 90 | 91 | //detect if init is needed 92 | if (i < JOYSTICK_DATA_SIZE) { 93 | delay(10); 94 | type = initExtension(); 95 | #ifdef DEBUG 96 | Serial.println("Init!"); 97 | #endif 98 | delay(10); 99 | } 100 | 101 | 102 | // Nunchuck 103 | uint8_t SX = data[0]; 104 | uint8_t SY = ~data[1]; 105 | uint16_t AX = (data[2] << 2) | (data[5] >> 2 & B11); 106 | uint16_t AY = (data[3] << 2) | (data[5] >> 4 & B11); 107 | uint16_t AZ = (data[4] << 2) | (data[5] >> 6 & B11); 108 | uint8_t BC = ~data[5] >> 1 & 1; 109 | uint8_t BZ = ~data[5] & 1; 110 | //http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck 111 | 112 | // Classic Controller 113 | uint8_t LX = (data[0] & 0x3f); 114 | uint8_t LY = (data[1] & 0x3f); 115 | uint8_t RX = ((data[0] & 0xc0) >> 3) + ((data[1] & 0xc0) >> 5) + ((data[2] & 0x80) >> 7); 116 | uint8_t RY = (data[2] & 0x1f); 117 | uint8_t BDU = ~data[5] & 1; 118 | uint8_t BDD = ~data[4] >> 6 & 1; 119 | uint8_t BDL = ~data[5] >> 1 & 1; 120 | uint8_t BDR = ~data[4] >> 7 & 1; 121 | uint8_t Bselect = ~data[4] >> 4 & 1; 122 | uint8_t BH = ~data[4] >> 3 & 1; 123 | uint8_t Bstart = ~data[4] >> 2 & 1; 124 | uint8_t BA = ~data[5] >> 4 & 1; 125 | uint8_t BB = ~data[5] >> 6 & 1; 126 | uint8_t BX = ~data[5] >> 3 & 1; 127 | uint8_t BY = ~data[5] >> 5 & 1; 128 | uint8_t BLT = ~data[4] >> 5 & 1; 129 | uint8_t BRT = ~data[4] >> 1 & 1; 130 | uint8_t BZL = ~data[5] >> 7 & 1; 131 | uint8_t BZR = ~data[5] >> 2 & 1; 132 | uint8_t LT = ((data[2] & 0x60) >> 2) + ((data[3] & 0xe0) >> 5); 133 | uint8_t RT = (data[3] & 0x1f); 134 | //http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller 135 | 136 | 137 | #ifdef DEBUG 138 | unsigned long d = micros()-t; 139 | Serial.print(d); 140 | #endif 141 | 142 | 143 | #ifdef DEBUG 144 | /*for (uint8_t i = 0; i < JOYSTICK_DATA_SIZE; i++) { 145 | Serial.print(data[i], HEX); 146 | Serial.print(" "); 147 | } 148 | Serial.println();*/ 149 | Serial.print(" type:"); Serial.print(type, HEX); 150 | 151 | if (type == NUNCHUCK) { 152 | // Nunchuck 153 | Serial.print(" SX:"); Serial.print(SX); 154 | Serial.print(" SY:"); Serial.print(SY); 155 | Serial.print(" AX:"); Serial.print(AX); 156 | Serial.print(" AY:"); Serial.print(AY); 157 | Serial.print(" AZ:"); Serial.print(AZ); 158 | Serial.print(" BC:"); Serial.print(BC); 159 | Serial.print(" BZ:"); Serial.print(BZ); 160 | } 161 | 162 | if (type == CLASSIC_CONTROLLER || type == CLASSIC_CONTROLLER_PRO) { 163 | // Classic Controller 164 | Serial.print(" LX:"); Serial.print(LX); 165 | Serial.print(" LY:"); Serial.print(LY); 166 | Serial.print(" RX:"); Serial.print(RX); 167 | Serial.print(" RY:"); Serial.print(RY); 168 | Serial.print(" BDU:"); Serial.print(BDU); 169 | Serial.print(" BDD:"); Serial.print(BDD); 170 | Serial.print(" BDL:"); Serial.print(BDL); 171 | Serial.print(" BDR:"); Serial.print(BDR); 172 | Serial.print(" Bselect:"); Serial.print(Bselect); 173 | Serial.print(" BH:"); Serial.print(BH); 174 | Serial.print(" Bstart:"); Serial.print(Bstart); 175 | Serial.print(" BA:"); Serial.print(BA); 176 | Serial.print(" BB:"); Serial.print(BB); 177 | Serial.print(" BX:"); Serial.print(BX); 178 | Serial.print(" BY:"); Serial.print(BY); 179 | Serial.print(" BLT:"); Serial.print(BLT); 180 | Serial.print(" BRT:"); Serial.print(BRT); 181 | Serial.print(" BZL:"); Serial.print(BZL); 182 | Serial.print(" BZR:"); Serial.print(BZR); 183 | Serial.print(" LT:"); Serial.print(LT); 184 | Serial.print(" RT:"); Serial.print(RT); 185 | } 186 | 187 | Serial.println(); 188 | #endif 189 | 190 | #ifdef DEBUG 191 | delay(100); 192 | #endif 193 | delayMicroseconds(1000); 194 | } 195 | -------------------------------------------------------------------------------- /X-Arcade/README.md: -------------------------------------------------------------------------------- 1 | # Arduino_X-Arcade_USB-adapter 2 | 3 | ## Keyboard 4 | https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/tree/master/PS2_Soarer_Converter 5 | 6 | ## 2 Joysticks 7 | 8 | "Old" PS/2 -version of X-arcade. 9 | ![X-Arcade](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/raw/master/Images/x-arcade-dual-joystick.jpg) 10 | 11 | ``` 12 | D9-connector -> Arduino Pro Micro 13 | 6 (data) -> 3 (PD0) 14 | 5 (clk) -> 2 (PD1) 15 | 1 (5V) -> Vcc 16 | 2 (PS/2-mode) -> Vcc 17 | 9 (5V) -> Vcc 18 | Shield -> GND 19 | 20 | Or 21 | 22 | PS/2-connector -> Arduino Pro Micro 23 | Data (green or blue) -> 3 (PD0) 24 | CLK (white or purple) -> 2 (PD1) 25 | 5V (red) -> 5V 26 | GND (black) -> GND 27 | 28 | ``` 29 | ### Library 30 | PS2KeyRaw 31 | 32 | Arduino Pro Micro (select Arduino Leonardo from Arduino IDE). 33 | 34 | PS/2 keyboard not needed. 35 | 36 | Takes only about 10mA. 37 | 38 | 39 | ## Linux: keyboard -> joystick 40 | - https://github.com/dmadison/ArduinoXInput 41 | - https://superuser.com/questions/837464/treat-usb-keyboard-as-gamepad 42 | - https://wiki.archlinux.org/title/Gamepad#Mimic_Xbox_360_controller_with_other_controllers 43 | 44 | ## RetroPie / Keyboard 45 | - https://retropie.org.uk/forum/topic/8987/guide-setting-up-a-retropie-controls-using-ipac2-controller-extensive-tutorial-preconfigured-files 46 | -------------------------------------------------------------------------------- /X-Arcade/x-arcade_c64.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define DATAPIN 3 5 | #define IRQPIN 2 6 | 7 | //#define DEBUG 8 | 9 | #define oUP1 5 10 | #define oDOWN1 6 11 | #define oLEFT1 7 12 | #define oRIGHT1 8 13 | #define oFIRE1 9 14 | 15 | #define oUP2 A0 16 | #define oDOWN2 15 17 | #define oLEFT2 14 18 | #define oRIGHT2 16 19 | #define oFIRE2 10 20 | 21 | #define UP1 0x75 22 | #define DOWN1 0x72 23 | #define LEFT1 0x6B 24 | #define RIGHT1 0x74 25 | #define START1 0x16 26 | #define SELECT1 0x26 27 | #define A1 0x12 28 | #define B1 0x1A 29 | #define C1 0x22 30 | #define X1 0x14 31 | #define Y1 0x11 32 | #define Z1 0x29 33 | #define L1 0x21 34 | #define R1 0x2E 35 | 36 | #define UP2 0x2D 37 | #define DOWN2 0x2B 38 | #define LEFT2 0x23 39 | #define RIGHT2 0x34 40 | #define START2 0x1E 41 | #define SELECT2 0x25 42 | #define A2 0x1D 43 | #define B2 0x24 44 | #define C2 0x54 45 | #define X2 0x1C 46 | #define Y2 0x1B 47 | #define Z2 0x15 48 | #define L2 0x5B 49 | #define R2 0x36 50 | 51 | uint8_t s1 = 0; 52 | uint8_t s2 = 0; 53 | uint8_t st1 = 0; 54 | uint8_t st2 = 0; 55 | uint8_t commando = 0; 56 | 57 | PS2KeyRaw keyboard; 58 | 59 | void setup() { 60 | keyboard.begin(DATAPIN, IRQPIN); 61 | Serial.begin(115200); 62 | Serial.println( "X-Arcade -> Atari -adapter" ); 63 | 64 | pinMode(oUP1, INPUT); 65 | pinMode(oDOWN1, INPUT); 66 | pinMode(oLEFT1, INPUT); 67 | pinMode(oRIGHT1, INPUT); 68 | pinMode(oFIRE1, INPUT); 69 | 70 | pinMode(oUP2, INPUT); 71 | pinMode(oDOWN2, INPUT); 72 | pinMode(oLEFT2, INPUT); 73 | pinMode(oRIGHT2, INPUT); 74 | pinMode(oFIRE2, INPUT); 75 | 76 | } 77 | 78 | void loop() { 79 | 80 | if (keyboard.available()) { 81 | // read the next key 82 | int c = keyboard.read(); 83 | uint8_t j; 84 | 85 | if (c == 0xF0) { 86 | #ifdef DEBUG 87 | Serial.print("0x"); Serial.print(c, HEX); Serial.print(" "); 88 | #endif 89 | while (!keyboard.available()) {} 90 | c = keyboard.read(); 91 | #ifdef DEBUG 92 | Serial.print("0x"); Serial.println(c, HEX); 93 | #endif 94 | clearDataC64(c); 95 | } else { 96 | #ifdef DEBUG 97 | Serial.print("0x"); Serial.println(c, HEX); 98 | #endif 99 | setDataC64(c); 100 | } 101 | 102 | if (s1 && s2) commando = 1; 103 | if (st1 && st2) commando = 0; 104 | 105 | } 106 | 107 | } 108 | 109 | 110 | #define SET64(p) pinMode(p, OUTPUT); break; 111 | #define UNSET64(p) pinMode(p, INPUT); break; 112 | 113 | inline void setDataC64(uint8_t c) { 114 | switch (c) { 115 | case SELECT1: 116 | s1 = 1; 117 | break; 118 | case START1: 119 | st1 = 1; 120 | break; 121 | case A1: 122 | case B1: 123 | SET64(oFIRE1); 124 | case X1: 125 | case Y1: 126 | if (commando) { 127 | SET64(oFIRE2); 128 | } else { 129 | SET64(oFIRE1); 130 | } 131 | case C1: 132 | SET64(oDOWN1); 133 | case Z1: 134 | SET64(oUP1); 135 | case L1: 136 | SET64(oLEFT1); 137 | case R1: 138 | SET64(oRIGHT1); 139 | case UP1: 140 | SET64(oUP1); 141 | case DOWN1: 142 | SET64(oDOWN1); 143 | case LEFT1: 144 | SET64(oLEFT1); 145 | case RIGHT1: 146 | SET64(oRIGHT1); 147 | case SELECT2: 148 | s2 = 1; 149 | break; 150 | case START2: 151 | st2 = 1; 152 | break; 153 | case A2: 154 | case B2: 155 | SET64(oFIRE2); 156 | case X2: 157 | case Y2: 158 | if (commando) { 159 | SET64(oFIRE1); 160 | } else { 161 | SET64(oFIRE2); 162 | } 163 | case C2: 164 | SET64(oDOWN2); 165 | case Z2: 166 | SET64(oUP2); 167 | case L2: 168 | SET64(oLEFT2); 169 | case R2: 170 | SET64(oRIGHT2); 171 | case UP2: 172 | SET64(oUP2); 173 | case DOWN2: 174 | SET64(oDOWN2); 175 | case LEFT2: 176 | SET64(oLEFT2); 177 | case RIGHT2: 178 | SET64(oRIGHT2); 179 | } 180 | } 181 | 182 | 183 | 184 | inline void clearDataC64(uint8_t c) { 185 | switch (c) { 186 | case SELECT1: 187 | s1 = 0; 188 | break; 189 | case START1: 190 | st1 = 0; 191 | break; 192 | case A1: 193 | case B1: 194 | UNSET64(oFIRE1); 195 | case X1: 196 | case Y1: 197 | if (commando) { 198 | UNSET64(oFIRE2); 199 | } else{ 200 | UNSET64(oFIRE1); 201 | } 202 | case C1: 203 | UNSET64(oDOWN1); 204 | case Z1: 205 | UNSET64(oUP1); 206 | case L1: 207 | UNSET64(oLEFT1); 208 | case R1: 209 | UNSET64(oRIGHT1); 210 | case UP1: 211 | UNSET64(oUP1); 212 | case DOWN1: 213 | UNSET64(oDOWN1); 214 | case LEFT1: 215 | UNSET64(oLEFT1); 216 | case RIGHT1: 217 | UNSET64(oRIGHT1); 218 | case SELECT2: 219 | s2 = 0; 220 | break; 221 | case START2: 222 | st2 = 0; 223 | break; 224 | case A2: 225 | case B2: 226 | UNSET64(oFIRE2); 227 | case X2: 228 | case Y2: 229 | if (commando) { 230 | UNSET64(oFIRE1); 231 | } else { 232 | UNSET64(oFIRE2); 233 | } 234 | case C2: 235 | UNSET64(oDOWN2); 236 | case Z2: 237 | UNSET64(oUP2); 238 | case L2: 239 | UNSET64(oLEFT2); 240 | case R2: 241 | UNSET64(oRIGHT2); 242 | case UP2: 243 | UNSET64(oUP2); 244 | case DOWN2: 245 | UNSET64(oDOWN2); 246 | case LEFT2: 247 | UNSET64(oLEFT2); 248 | case RIGHT2: 249 | UNSET64(oRIGHT2); 250 | } 251 | } 252 | 253 | -------------------------------------------------------------------------------- /XBox360_XInput/README.md: -------------------------------------------------------------------------------- 1 | 2 | Based on https://github.com/dmadison/ArduinoXInput 3 | 4 | - Install Arduino IDE hardware: 5 | https://github.com/dmadison/ArduinoXInput_AVR 6 | 7 | - Install Arduino IDE library from Library manager: 8 | XInput by David Madison 9 | 10 | - Tester from Windows Store: 11 | Game Controller Tester 12 | 13 | - Online tester: https://gamepad-tester.com/ 14 | 15 | - Doesn't go to flashing mode automatically. Connect RST to GND to get Arduino Pro Micro to programming mode. 16 | 17 | ## PsxNewLib 18 | - https://github.com/SukkoPera/PsxNewLib (from Arduino IDE library manager 6.11.2021: 0.4.0) 19 | - https://github.com/SukkoPera/PsxControllerShield 20 | - https://github.com/SukkoPera/PsxControllerShield/blob/master/doc/schematics.pdf 21 | - (my green dualshock analog controller: SCPH-1200) (Controller Type is: Guitar Hero)? 22 | - (my black dualshock 2 controller: SCPH-10010) (Controller Type is: Dual Shock) 23 | - https://store.curiousinventor.com/guides/PS2 24 | - https://github.com/SukkoPera/PsxNewLib/issues/12 25 | - https://github.com/dmadison/NintendoExtensionCtrl 26 | 27 | PSX | SPI | Uno | Pro Micro | notes 28 | --- | --- | --- | --- | --- 29 | 1 DATA (brown ![#brown](https://via.placeholder.com/10/c68c53/000000?text=+)) | MISO → | D12 (PB4) | D14 (PB3) | Use 1k pullup resistor to 3.3V! 30 | 2 CMD (orange ![#orange](https://via.placeholder.com/10/ff8000/000000?text=+)) | MOSI ← | D11 (PB3) | D16 (PB2) | 31 | 3 Vibration power (7.2-9V) (grey ![#grey](https://via.placeholder.com/10/999999/000000?text=+)) | | | | 32 | 4 GND (black ![#black](https://via.placeholder.com/10/000000/000000?text=+)) | | GND | GND | 33 | 5 VCC (3.3V) (red ![#red](https://via.placeholder.com/10/ff0000/000000?text=+)) | | VCC | VCC | 34 | 6 ATT (yellow ![#yellow](https://via.placeholder.com/10/ffff00/000000?text=+)) | SS ← | D10 (PB2) | D10 (PB6) | (pro micro: SS would be D17 (PB0/RX_LED), but there is no pin D17) 35 | 7 CLK (blue ![#blue](https://via.placeholder.com/10/0000ff/000000?text=+)) | SCK ← | D13 (PB5) | D15 (PB1) | 36 | 8 unknown (white ![#white](https://via.placeholder.com/10/ffffff/000000?text=+)) | | | | 37 | 9 ACK (green ![#green](https://via.placeholder.com/10/00ff00/000000?text=+)) | | | | (when this is needed?) 38 | **Wii** | | | | | 39 | 1 VCC (3.3V) (red ![#red](https://via.placeholder.com/10/ff0000/000000?text=+)) | | | | 40 | 2 SCL (yellow ![#yellow](https://via.placeholder.com/10/ffff00/000000?text=+)) | | A5 (PC5) | D3 (PD0) | 41 | 3 Detect device (3.3V) (black ![#black](https://via.placeholder.com/10/000000/000000?text=+)) | | | D4 (PD4) | Use 10k pulldown resistor to GND! 42 | 4 NC | | | | 43 | 5 SDA (green ![#green](https://via.placeholder.com/10/00ff00/000000?text=+)) | | A4 (PC4) | D2 (PD1) | 44 | 6 GND (white ![#white](https://via.placeholder.com/10/ffffff/000000?text=+)) | | | | 45 | 46 | ### TODO 47 | - Analog triggers for Dualshock 2 48 | - Name in Windows: Controller (Arduino Leonardo), with real XB360 controller: Controller (XBOX 360 for Windows)? Can it be changed in C:\Program Files (x86)\Arduino\hardware\xinput\avr\boards.txt? 49 | -------------------------------------------------------------------------------- /XBox360_XInput/RetroJoystickAdapter_Playstation_XB360 (old).ino: -------------------------------------------------------------------------------- 1 | #define XINPUT 2 | 3 | #ifdef XINPUT 4 | #include 5 | #endif 6 | 7 | #ifndef XINPUT 8 | #define DEBUG 9 | #endif 10 | //#define DEBUG //doesn't work with XInput 11 | 12 | // 5V (red) 13 | // GND (black) 14 | #define DATA1 2 // (brown) 15 | #define CMD1 3 // (orange) 16 | #define ATT1 4 // (yellow) 17 | #define CLK1 5 // (blue) 18 | 19 | #define JOYSTICK_STATE_SIZE 6 20 | 21 | //data[0] 22 | //bit0 = select 23 | //bit1 = L3 24 | //bit2 = R3 25 | //bit3 = start 26 | //bit4 = up 27 | //bit5 = right 28 | //bit6 = down 29 | //bit7 = left 30 | //data[1} 31 | //bit0 = L2 32 | //bit1 = R2 33 | //bit2 = L1 34 | //bit3 = R1 35 | //bit4 = T 36 | //bit5 = O 37 | //bit6 = X 38 | //bit7 = S 39 | 40 | #define PS_select (data[0] & ( 1 << 0 )) 41 | #define PS_L3 (data[0] & ( 1 << 1 )) 42 | #define PS_R3 (data[0] & ( 1 << 2 )) 43 | #define PS_start (data[0] & ( 1 << 3 )) 44 | #define PS_up (data[0] & ( 1 << 4 )) 45 | #define PS_right (data[0] & ( 1 << 5 )) 46 | #define PS_down (data[0] & ( 1 << 6 )) 47 | #define PS_left (data[0] & ( 1 << 7 )) 48 | 49 | #define PS_L2 (data[1] & ( 1 << 0 )) 50 | #define PS_R2 (data[1] & ( 1 << 1 )) 51 | #define PS_L1 (data[1] & ( 1 << 2 )) 52 | #define PS_R1 (data[1] & ( 1 << 3 )) 53 | #define PS_T (data[1] & ( 1 << 4 )) 54 | #define PS_O (data[1] & ( 1 << 5 )) 55 | #define PS_X (data[1] & ( 1 << 6 )) 56 | #define PS_S (data[1] & ( 1 << 7 )) 57 | #define PS_LX (((uint16_t)data[4]*257)-32768) // 0..255 -> -32768..32767 58 | #define PS_LY (((255-(uint16_t)data[5])*257)-32768) // 0..255 -> 32767..-32768 59 | #define PS_RX (((uint16_t)data[2]*257)-32768) // 0..255 -> -32768..32767 60 | #define PS_RY (((255-(uint16_t)data[3])*257)-32768) // 0..255 -> 32767..-32768 61 | 62 | uint8_t head; 63 | uint8_t type; 64 | uint8_t padding; 65 | uint8_t data[JOYSTICK_STATE_SIZE]; 66 | uint8_t olddata[JOYSTICK_STATE_SIZE]; 67 | uint8_t flag; 68 | 69 | uint8_t shift(uint8_t _dataOut) // Does the actual shifting, both in and out simultaneously 70 | { 71 | uint8_t _temp = 0; 72 | uint8_t _dataIn = 0; 73 | uint8_t _delay = 6; //2 unstable; //clock 250kHz 74 | 75 | delayMicroseconds(100); //max acknowledge waiting time 100us 76 | for (uint8_t _i = 0; _i <= 7; _i++) { 77 | 78 | if ( _dataOut & (1 << _i) ) // write bit 79 | digitalWrite(CMD1, HIGH); 80 | else 81 | digitalWrite(CMD1, LOW); 82 | 83 | digitalWrite(CLK1, LOW); // read bit 84 | delayMicroseconds(_delay); 85 | _temp = digitalRead(DATA1); 86 | if (_temp) { 87 | _dataIn = _dataIn | (B00000001 << _i); 88 | } 89 | 90 | digitalWrite(CLK1, HIGH); 91 | delayMicroseconds(_delay); 92 | } 93 | return _dataIn; 94 | } 95 | 96 | void setup() { 97 | pinMode(DATA1, INPUT_PULLUP); 98 | pinMode(CMD1, OUTPUT); 99 | pinMode(ATT1, OUTPUT); 100 | pinMode(CLK1, OUTPUT); 101 | 102 | #ifdef DEBUG 103 | Serial.begin(115200); 104 | while(!Serial); 105 | #endif 106 | 107 | #ifdef XINPUT 108 | XInput.setAutoSend(false); 109 | XInput.begin(); 110 | #endif 111 | 112 | olddata[0] = 0xff; 113 | olddata[1] = 0xff; 114 | 115 | } 116 | 117 | void loop() { 118 | // http://problemkaputt.de/psx-spx.htm#controllerandmemorycardsignals 119 | // first: read gamepad normally 120 | digitalWrite(ATT1, LOW); 121 | //digitalWrite(ATT2, LOW); 122 | head = shift(0x01); 123 | type = shift(0x42); 124 | padding = shift(0x01); //read multitap in next command 125 | data[0] = ~shift(0x00); //buttons 126 | data[1] = ~shift(0x00); //buttons 127 | data[2] = shift(0x00); //right analog 128 | data[3] = shift(0x00); //right analog 129 | data[4] = shift(0x00); //left analog 130 | data[5] = shift(0x00); //left analog 131 | digitalWrite(ATT1, HIGH); 132 | 133 | #ifdef DEBUG 134 | Serial.print(" type: 0x"); Serial.print(type, HEX); 135 | Serial.print(" data: b"); Serial.print(data[0], BIN); 136 | Serial.print(" b"); Serial.print(data[1], BIN); 137 | Serial.print(" 0x"); Serial.print(data[2], HEX); 138 | Serial.print(" 0x"); Serial.print(data[3], HEX); 139 | Serial.print(" 0x"); Serial.print(data[4], HEX); 140 | Serial.print(" 0x"); Serial.print(data[5], HEX); 141 | Serial.println(); 142 | Serial.flush(); 143 | delay(500); 144 | #endif 145 | 146 | #ifdef XINPUT 147 | if (data[0] != olddata[0]) { 148 | if (PS_select) XInput.press(BUTTON_BACK); else XInput.release(BUTTON_BACK); //btn_7 149 | if (PS_start) XInput.press(BUTTON_START); else XInput.release(BUTTON_START); //btn_8 150 | if (PS_L3) XInput.press(BUTTON_L3); else XInput.release(BUTTON_L3); //btn_9 151 | if (PS_R3) XInput.press(BUTTON_R3); else XInput.release(BUTTON_R3); //btn_10 152 | XInput.setDpad(PS_up, PS_down, PS_left, PS_right); 153 | olddata[0] = data[0]; 154 | } 155 | if (data[1] != olddata[1]) { 156 | if (PS_X) XInput.press(BUTTON_A); else XInput.release(BUTTON_A); //btn_1 157 | if (PS_O) XInput.press(BUTTON_B); else XInput.release(BUTTON_B); //btn_2 158 | if (PS_S) XInput.press(BUTTON_X); else XInput.release(BUTTON_X); //btn_3 159 | if (PS_T) XInput.press(BUTTON_Y); else XInput.release(BUTTON_Y); //btn_4 160 | if (PS_L1) XInput.press(BUTTON_LB); else XInput.release(BUTTON_LB); //btn_5 161 | if (PS_R1) XInput.press(BUTTON_RB); else XInput.release(BUTTON_RB); //btn_6 162 | if (PS_L2) XInput.setTrigger(TRIGGER_LEFT, 255); else XInput.setTrigger(TRIGGER_LEFT, 0); 163 | if (PS_R2) XInput.setTrigger(TRIGGER_RIGHT, 255); else XInput.setTrigger(TRIGGER_RIGHT, 0); 164 | olddata[1] = data[1]; 165 | } 166 | XInput.setJoystick(JOY_LEFT, PS_LX, PS_LY); //left-right: -32768..32767, down-up: 32767..-32768 167 | XInput.setJoystick(JOY_RIGHT, PS_RX, PS_RY); //left-right: -32768..32767, down-up: 32767..-32768 168 | #endif 169 | 170 | XInput.send(); 171 | 172 | delayMicroseconds(1000); 173 | 174 | 175 | } 176 | -------------------------------------------------------------------------------- /XBox360_XInput/RetroJoystickAdapter_PsxNewLib_XB360.ino: -------------------------------------------------------------------------------- 1 | // https://github.com/SukkoPera/PsxNewLib 2 | // https://github.com/dmadison/ArduinoXInput 3 | 4 | #define XINPUT 5 | 6 | #ifdef XINPUT 7 | #include 8 | #endif 9 | 10 | #ifndef XINPUT 11 | #define DEBUG 12 | #endif 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | typedef const __FlashStringHelper * FlashStr; 19 | typedef const byte* PGM_BYTES_P; 20 | #define PSTR_TO_F(s) reinterpret_cast (s) 21 | 22 | // This can be changed freely but please see above 23 | const byte PIN_PS2_ATT = 10; 24 | 25 | const byte PIN_BUTTONPRESS = A0; 26 | const byte PIN_HAVECONTROLLER = A1; 27 | 28 | bool dirty = true; 29 | 30 | const char buttonSelectName[] PROGMEM = "Select"; 31 | const char buttonL3Name[] PROGMEM = "L3"; 32 | const char buttonR3Name[] PROGMEM = "R3"; 33 | const char buttonStartName[] PROGMEM = "Start"; 34 | const char buttonUpName[] PROGMEM = "Up"; 35 | const char buttonRightName[] PROGMEM = "Right"; 36 | const char buttonDownName[] PROGMEM = "Down"; 37 | const char buttonLeftName[] PROGMEM = "Left"; 38 | const char buttonL2Name[] PROGMEM = "L2"; 39 | const char buttonR2Name[] PROGMEM = "R2"; 40 | const char buttonL1Name[] PROGMEM = "L1"; 41 | const char buttonR1Name[] PROGMEM = "R1"; 42 | const char buttonTriangleName[] PROGMEM = "Triangle"; 43 | const char buttonCircleName[] PROGMEM = "Circle"; 44 | const char buttonCrossName[] PROGMEM = "Cross"; 45 | const char buttonSquareName[] PROGMEM = "Square"; 46 | 47 | 48 | #define PS_select (psxButtons & ( 1 << 0 )) 49 | #define PS_L3 (psxButtons & ( 1 << 1 )) 50 | #define PS_R3 (psxButtons & ( 1 << 2 )) 51 | #define PS_start (psxButtons & ( 1 << 3 )) 52 | #define PS_up (psxButtons & ( 1 << 4 )) 53 | #define PS_right (psxButtons & ( 1 << 5 )) 54 | #define PS_down (psxButtons & ( 1 << 6 )) 55 | #define PS_left (psxButtons & ( 1 << 7 )) 56 | #define PS_L2 (psxButtons & ( 1 << 8 )) 57 | #define PS_R2 (psxButtons & ( 1 << 9 )) 58 | #define PS_L1 (psxButtons & ( 1 << 10 )) 59 | #define PS_R1 (psxButtons & ( 1 << 11 )) 60 | #define PS_T (psxButtons & ( 1 << 12 )) 61 | #define PS_O (psxButtons & ( 1 << 13 )) 62 | #define PS_X (psxButtons & ( 1 << 14 )) 63 | #define PS_S (psxButtons & ( 1 << 15 )) 64 | 65 | #define PS_LX (((uint16_t)lx*257)-32768) // 0..255 -> -32768..32767 66 | #define PS_LY (((255-(uint16_t)ly)*257)-32768) // 0..255 -> 32767..-32768 67 | #define PS_RX (((uint16_t)rx*257)-32768) // 0..255 -> -32768..32767 68 | #define PS_RY (((255-(uint16_t)ry)*257)-32768) // 0..255 -> 32767..-32768 69 | //psxnelib: left-right: 0...255 down-up: 255...0, xinput: left-right: -32768..32767, down-up: 32767..-32768 70 | 71 | const char* const psxButtonNames[PSX_BUTTONS_NO] PROGMEM = { 72 | buttonSelectName, 73 | buttonL3Name, 74 | buttonR3Name, 75 | buttonStartName, 76 | buttonUpName, 77 | buttonRightName, 78 | buttonDownName, 79 | buttonLeftName, 80 | buttonL2Name, 81 | buttonR2Name, 82 | buttonL1Name, 83 | buttonR1Name, 84 | buttonTriangleName, 85 | buttonCircleName, 86 | buttonCrossName, 87 | buttonSquareName 88 | }; 89 | 90 | byte psxButtonToIndex (PsxButtons psxButtons) { 91 | byte i; 92 | 93 | for (i = 0; i < PSX_BUTTONS_NO; ++i) { 94 | if (psxButtons & 0x01) { 95 | break; 96 | } 97 | 98 | psxButtons >>= 1U; 99 | } 100 | 101 | return i; 102 | } 103 | 104 | FlashStr getButtonName (PsxButtons psxButton) { 105 | FlashStr ret = F(""); 106 | 107 | byte b = psxButtonToIndex (psxButton); 108 | if (b < PSX_BUTTONS_NO) { 109 | PGM_BYTES_P bName = reinterpret_cast (pgm_read_ptr (&(psxButtonNames[b]))); 110 | ret = PSTR_TO_F (bName); 111 | } 112 | 113 | return ret; 114 | } 115 | 116 | void dumpButtons (PsxButtons psxButtons) { 117 | static PsxButtons lastB = 0; 118 | 119 | if (psxButtons != lastB) { 120 | lastB = psxButtons; // Save it before we alter it 121 | 122 | #ifdef DEBUG 123 | Serial.print (F("Pressed: ")); 124 | 125 | for (byte i = 0; i < PSX_BUTTONS_NO; ++i) { 126 | byte b = psxButtonToIndex (psxButtons); 127 | if (b < PSX_BUTTONS_NO) { 128 | PGM_BYTES_P bName = reinterpret_cast (pgm_read_ptr (&(psxButtonNames[b]))); 129 | Serial.print (PSTR_TO_F (bName)); 130 | } 131 | 132 | psxButtons &= ~(1 << b); 133 | 134 | if (psxButtons != 0) { 135 | Serial.print (F(", ")); 136 | } 137 | } 138 | Serial.println (); 139 | #endif 140 | 141 | #ifdef XINPUT 142 | if (PS_select) XInput.press(BUTTON_BACK); else XInput.release(BUTTON_BACK); //btn_7 143 | if (PS_start) XInput.press(BUTTON_START); else XInput.release(BUTTON_START); //btn_8 144 | if (PS_L3) XInput.press(BUTTON_L3); else XInput.release(BUTTON_L3); //btn_9 145 | if (PS_R3) XInput.press(BUTTON_R3); else XInput.release(BUTTON_R3); //btn_10 146 | XInput.setDpad(PS_up, PS_down, PS_left, PS_right); 147 | if (PS_X) XInput.press(BUTTON_A); else XInput.release(BUTTON_A); //btn_1 148 | if (PS_O) XInput.press(BUTTON_B); else XInput.release(BUTTON_B); //btn_2 149 | if (PS_S) XInput.press(BUTTON_X); else XInput.release(BUTTON_X); //btn_3 150 | if (PS_T) XInput.press(BUTTON_Y); else XInput.release(BUTTON_Y); //btn_4 151 | if (PS_L1) XInput.press(BUTTON_LB); else XInput.release(BUTTON_LB); //btn_5 152 | if (PS_R1) XInput.press(BUTTON_RB); else XInput.release(BUTTON_RB); //btn_6 153 | if (PS_L2) XInput.setTrigger(TRIGGER_LEFT, 255); else XInput.setTrigger(TRIGGER_LEFT, 0); 154 | if (PS_R2) XInput.setTrigger(TRIGGER_RIGHT, 255); else XInput.setTrigger(TRIGGER_RIGHT, 0); 155 | dirty = true; 156 | #endif 157 | } 158 | } 159 | 160 | void dumpAnalog (const char *str, const byte x, const byte y) { 161 | #ifdef DEBUG 162 | Serial.print (str); 163 | Serial.print (F(" analog: x = ")); 164 | Serial.print (x); 165 | Serial.print (F(", y = ")); 166 | Serial.println (y); 167 | #endif 168 | } 169 | 170 | 171 | 172 | const char ctrlTypeUnknown[] PROGMEM = "Unknown"; 173 | const char ctrlTypeDualShock[] PROGMEM = "Dual Shock"; 174 | const char ctrlTypeDsWireless[] PROGMEM = "Dual Shock Wireless"; 175 | const char ctrlTypeGuitHero[] PROGMEM = "Guitar Hero"; 176 | const char ctrlTypeOutOfBounds[] PROGMEM = "(Out of bounds)"; 177 | 178 | const char* const controllerTypeStrings[PSCTRL_MAX + 1] PROGMEM = { 179 | ctrlTypeUnknown, 180 | ctrlTypeDualShock, 181 | ctrlTypeDsWireless, 182 | ctrlTypeGuitHero, 183 | ctrlTypeOutOfBounds 184 | }; 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | PsxControllerHwSpi psx; 193 | 194 | boolean haveController = false; 195 | 196 | void setup () { 197 | fastPinMode (PIN_BUTTONPRESS, OUTPUT); 198 | fastPinMode (PIN_HAVECONTROLLER, OUTPUT); 199 | 200 | delay (300); 201 | 202 | #ifdef DEBUG 203 | Serial.begin (115200); 204 | while(!Serial); 205 | Serial.println (F("Ready!")); 206 | #endif 207 | 208 | #ifdef XINPUT 209 | XInput.setAutoSend(false); 210 | XInput.begin(); 211 | #endif 212 | } 213 | 214 | void loop () { 215 | static byte slx, sly, srx, sry, sl2, sr2; 216 | fastDigitalWrite (PIN_HAVECONTROLLER, haveController); 217 | 218 | if (!haveController) { 219 | if (psx.begin ()) { 220 | #ifdef DEBUG 221 | Serial.println (F("Controller found!")); 222 | #endif 223 | delay (300); 224 | if (!psx.enterConfigMode ()) { 225 | #ifdef DEBUG 226 | Serial.println (F("Cannot enter config mode")); 227 | #endif 228 | } else { 229 | PsxControllerType ctype = psx.getControllerType (); 230 | PGM_BYTES_P cname = reinterpret_cast (pgm_read_ptr (&(controllerTypeStrings[ctype < PSCTRL_MAX ? static_cast (ctype) : PSCTRL_MAX]))); 231 | #ifdef DEBUG 232 | Serial.print (F("Controller Type is: ")); 233 | Serial.println (PSTR_TO_F (cname)); 234 | #endif 235 | 236 | if (!psx.enableAnalogSticks ()) { 237 | #ifdef DEBUG 238 | Serial.println (F("Cannot enable analog sticks")); 239 | #endif 240 | } 241 | 242 | //~ if (!psx.setAnalogMode (false)) { 243 | //~ Serial.println (F("Cannot disable analog mode")); 244 | //~ } 245 | //~ delay (10); 246 | 247 | if (!psx.enableAnalogButtons ()) { 248 | #ifdef DEBUG 249 | Serial.println (F("Cannot enable analog buttons")); 250 | #endif 251 | } 252 | 253 | if (!psx.exitConfigMode ()) { 254 | #ifdef DEBUG 255 | Serial.println (F("Cannot exit config mode")); 256 | #endif 257 | } 258 | } 259 | 260 | haveController = true; 261 | } 262 | } else { 263 | if (!psx.read ()) { 264 | #ifdef DEBUG 265 | Serial.println (F("Controller lost :(")); 266 | #endif 267 | haveController = false; 268 | } else { 269 | fastDigitalWrite (PIN_BUTTONPRESS, !!psx.getButtonWord ()); 270 | dumpButtons (psx.getButtonWord ()); 271 | 272 | byte lx, ly; 273 | psx.getLeftAnalog (lx, ly); 274 | if (lx != slx || ly != sly) { 275 | dumpAnalog ("Left", lx, ly); 276 | #ifdef XINPUT 277 | XInput.setJoystick(JOY_LEFT, PS_LX, PS_LY); //psxnelib: left-right: 0...255 down-up: 255...0, xinput: left-right: -32768..32767, down-up: 32767..-32768 278 | dirty = true; 279 | #endif 280 | slx = lx; 281 | sly = ly; 282 | } 283 | 284 | byte rx, ry; 285 | psx.getRightAnalog (rx, ry); 286 | if (rx != srx || ry != sry) { 287 | dumpAnalog ("Right", rx, ry); 288 | #ifdef XINPUT 289 | XInput.setJoystick(JOY_RIGHT, PS_RX, PS_RY); //psxnelib: left-right: 0...255 down-up: 255...0, xinput: left-right: -32768..32767, down-up: 32767..-32768 290 | dirty = true; 291 | #endif 292 | srx = rx; 293 | sry = ry; 294 | } 295 | 296 | byte l2 = psx.getAnalogButton(PSAB_L2); 297 | if (l2 != 0 && l2 != sl2) { 298 | #ifdef DEBUG 299 | Serial.println(l2); 300 | #endif 301 | XInput.setTrigger(TRIGGER_LEFT, l2); 302 | dirty = true; 303 | sl2 = l2; 304 | } 305 | 306 | byte r2 = psx.getAnalogButton(PSAB_R2); 307 | if (r2 != 0 && r2 != sr2) { 308 | #ifdef DEBUG 309 | Serial.println(r2); 310 | #endif 311 | XInput.setTrigger(TRIGGER_RIGHT, r2); 312 | dirty = true; 313 | sr2 = r2; 314 | } 315 | 316 | } 317 | } 318 | 319 | 320 | //delay (1000 / 60); 321 | #ifdef XINPUT 322 | if (dirty) { 323 | XInput.send(); 324 | dirty = false; 325 | } 326 | #endif 327 | 328 | //delayMicroseconds(1000); 329 | delay(10); 330 | 331 | } 332 | -------------------------------------------------------------------------------- /atari/Hardware_Atari-SMS-Genesis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/atari/Hardware_Atari-SMS-Genesis.jpg -------------------------------------------------------------------------------- /atari/README.md: -------------------------------------------------------------------------------- 1 | # Atari 2 | 3 | ## What do I need? 4 | - You will need one [Atmega32u4](https://pt.aliexpress.com/item/New-Pro-Micro-ATmega32U4-5V-16MHz-Module-with-2-row-pin-header-For-Leonardo-best-quality/32273120508.html?spm=2114.13010608.0.0.Uv843y&detailNewVersion=&categoryId=400103) (e.g. Arduino Leonardo). 5 | 6 | ![atari-adapter](Hardware_Atari-SMS-Genesis.jpg) 7 | -------------------------------------------------------------------------------- /boards.txt: -------------------------------------------------------------------------------- 1 | ############################################################## 2 | 3 | retroadapter.name=Arduino Pro Micro Retroadapter 4 | 5 | retroadapter.upload.tool=arduino:avrdude 6 | retroadapter.upload.protocol=avr109 7 | retroadapter.upload.maximum_size=28672 8 | retroadapter.upload.maximum_data_size=2560 9 | retroadapter.upload.speed=57600 10 | retroadapter.upload.disable_flushing=true 11 | retroadapter.upload.use_1200bps_touch=true 12 | retroadapter.upload.wait_for_upload_port=true 13 | 14 | retroadapter.bootloader.tool=arduino:avrdude 15 | retroadapter.bootloader.low_fuses=0xff 16 | retroadapter.bootloader.high_fuses=0xd8 17 | retroadapter.bootloader.extended_fuses=0xcb 18 | retroadapter.bootloader.file=..\..\..\..\..\..\..\..\..\..\Program Files (x86)\Arduino\hardware\arduino\avr\bootloaders\caterina\Caterina-leonardo.hex 19 | retroadapter.bootloader.unlock_bits=0x3F 20 | retroadapter.bootloader.lock_bits=0x2F 21 | 22 | retroadapter.build.mcu=atmega32u4 23 | retroadapter.build.f_cpu=16000000L 24 | retroadapter.build.vid=0x8282 25 | retroadapter.build.pid=0x3201 26 | retroadapter.build.usb_product="Arduino Retroadapter" 27 | retroadapter.build.usb_manufacturer="mcgurk" 28 | retroadapter.build.board=AVR_LEONARDO 29 | retroadapter.build.core=arduino:arduino 30 | retroadapter.build.variant=arduino:leonardo 31 | retroadapter.build.extra_flags={build.usb_flags} 32 | 33 | ############################################################## 34 | -------------------------------------------------------------------------------- /megadrive/README.md: -------------------------------------------------------------------------------- 1 | # MegaDrive / Genesis Manual 2 | 3 | ## What do I need? 4 | - You will need one [Atmega32u4](https://pt.aliexpress.com/item/New-Pro-Micro-ATmega32U4-5V-16MHz-Module-with-2-row-pin-header-For-Leonardo-best-quality/32273120508.html?spm=2114.13010608.0.0.Uv843y&detailNewVersion=&categoryId=400103) (e.g. Arduino Leonardo). 5 | - [Two DB9 ports](https://pt.aliexpress.com/item/Type-DR9-90-degrees-bend-DR9-male-head-needle-serial-port-and-seat-DB9-RS232-9/32706812789.html?spm=2114.13010608.0.0.1YtDRt), better if you find a 90º version. 6 | - Original Mega Drive Controller 7 | - Follow step by step the how-to [`Long version`](https://github.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter#long-version) 8 | - Use the file [RetroJoystickAdapter_Megadrive.ino](../RetroJoystickAdapter_Megadrive.ino), comment the fist set of pins, and uncomment the second that says `// if you use two DB9 connectors solded back to back on your ATmega32u4, you should use this inputs`. 9 | 10 | ## Schematics and pinout 11 | 12 | You should print this schema to guide you during the soldering process. 13 | 14 | ![megadrive-adapter](pinout.jpg) 15 | 16 | ## Final product 17 | 18 | Here are Adriano's examples of his assembly. 19 | 20 | ![solded-adapter](atmega_solded.jpg) 21 | ![solded-back-adapter](atmega_solded_back.jpg) 22 | ![testing-adapter](atmega_testing.jpg) 23 | 24 | Here are the Jarno's examples of his assembly. 25 | 26 | ![Hardware_Atari-SMS-Genesis](../atari/Hardware_Atari-SMS-Genesis.jpg) 27 | ![sega_genesis_adapter](../Images/sega_genesis_adapter.jpg) 28 | -------------------------------------------------------------------------------- /megadrive/atmega_solded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/megadrive/atmega_solded.jpg -------------------------------------------------------------------------------- /megadrive/atmega_solded_back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/megadrive/atmega_solded_back.jpg -------------------------------------------------------------------------------- /megadrive/atmega_testing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/megadrive/atmega_testing.jpg -------------------------------------------------------------------------------- /megadrive/pinout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/megadrive/pinout.jpg -------------------------------------------------------------------------------- /nes/README.md: -------------------------------------------------------------------------------- 1 | ## Nes 2 | 3 | ![nes-adapter](nes.png) 4 | -------------------------------------------------------------------------------- /nes/nes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/nes/nes.png -------------------------------------------------------------------------------- /playstation/README.md: -------------------------------------------------------------------------------- 1 | ## Sony Playstation 2 | 3 | ## What do I need? 4 | - You will need one [Atmega32u4](https://pt.aliexpress.com/item/New-Pro-Micro-ATmega32U4-5V-16MHz-Module-with-2-row-pin-header-For-Leonardo-best-quality/32273120508.html?spm=2114.13010608.0.0.Uv843y&detailNewVersion=&categoryId=400103) (e.g. Arduino Leonardo). 5 | 6 | ![playstation-adapter](Sony_Playstation_Multitap.jpg) 7 | 8 | ## Wiring the Controller 9 | As the following picture from the [amazing CuriousInventor PS2 Interface Guide](https://store.curiousinventor.com/guides/PS2) shows, PlayStation controllers use 9 pins: 10 | 11 | ![PS2 Controller Pinout](https://store.curiousinventor.com/wp-content/uploads/2019/09/wiring.jpg) 12 | 13 | | Pin # | Signal | Direction | Notes | 14 | |-------|-------------|---------------------------|----------------| 15 | | 1 | Data | Controller -> PlayStation | Open Collector | 16 | | 2 | Command | PlayStation -> Controller | | 17 | | 3 | Motor Power | | 7.5V | 18 | | 4 | Ground | | | 19 | | 5 | Power | | 3.6V | 20 | | 6 | Attention | PlayStation -> Controller | | 21 | | 7 | Clock | PlayStation -> Controller | | 22 | | 8 | (Unknown) | | | 23 | | 9 | Acknowledge | Controller -> PlayStation | Open Collector | 24 | 25 | **You are advised not to rely on wire colors, but rather on pin positions**. The wires in the image come from an official Sony controller, I expect their colors to be fairly consistent among all Sony controllers, but you shouldn't really trust them. 26 | 27 | -- documentation taken from https://github.com/SukkoPera/PsxNewLib 28 | -------------------------------------------------------------------------------- /playstation/Sony_Playstation_Multitap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcgurk/Arduino-USB-HID-RetroJoystickAdapter/1de6e3a70abea8dd9f7a74a8b391ddbc12d1e299/playstation/Sony_Playstation_Multitap.jpg -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | #Note 2 | This files are not maintained, and are only for test proposes. 3 | 4 | ## psx 5 | needs psx-library from here: 6 | http://playground.arduino.cc/Main/PSXLibrary 7 | (dualshock-features not supported) 8 | -------------------------------------------------------------------------------- /test/RetroJoystickAdapter-2xNES.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Description: Interfacing a 2-port NES controller with a PC with an Arduino. 3 | Coded by: Prodigity 4 | Date: 1 December 2011 5 | Revision: V0.93 (beta) 6 | Modified by: Matt Booth (20 December 2014) 7 | Adapted for USB (ATmega32U4, 5V) by Kurg 3.9.2016 8 | */ 9 | 10 | #include "Joystick2.h" 11 | 12 | #define EVENTS_TOTAL 4+2+2 //4 directions, 2 fire-buttons and Start + Select 13 | 14 | uint8_t lastStatusPort1[EVENTS_TOTAL]; 15 | uint8_t newStatusPort1[EVENTS_TOTAL]; 16 | uint8_t lastStatusPort2[EVENTS_TOTAL]; 17 | uint8_t newStatusPort2[EVENTS_TOTAL]; 18 | 19 | 20 | #define CLOCK1 7 21 | #define LATCH1 8 22 | #define DATA1 9 23 | 24 | #define CLOCK2 2 25 | #define LATCH2 3 26 | #define DATA2 4 27 | 28 | // http://www.mit.edu/~tarvizo/nes-controller.html 29 | 30 | 31 | void setup() { 32 | 33 | //clear statusarrays (1=OFF, 0=ON) 34 | for (uint8_t i = 0; i < EVENTS_TOTAL; i++) { 35 | lastStatusPort1[i] = 1; 36 | newStatusPort1[i] = 1; 37 | lastStatusPort2[i] = 1; 38 | newStatusPort2[i] = 1; 39 | } 40 | 41 | pinMode(LATCH1, OUTPUT); 42 | pinMode(CLOCK1, OUTPUT); 43 | pinMode(DATA1, INPUT); 44 | pinMode(LATCH2, OUTPUT); 45 | pinMode(CLOCK2, OUTPUT); 46 | pinMode(DATA2, INPUT); 47 | 48 | Joystick[0].begin(false); 49 | Joystick[1].begin(false); 50 | } 51 | 52 | 53 | uint8_t flag1 = 0; 54 | uint8_t flag2 = 0; 55 | 56 | 57 | void loop() { 58 | 59 | ReadNESjoy(); 60 | 61 | //check for changes - do not raise a flag if nothing changes 62 | for (uint8_t i=0; i < EVENTS_TOTAL; i++) { 63 | if (newStatusPort1[i] != lastStatusPort1[i]) { 64 | lastStatusPort1[i] = newStatusPort1[i]; 65 | flag1 = 1; 66 | } 67 | if (newStatusPort2[i] != lastStatusPort2[i]) { 68 | lastStatusPort2[i] = newStatusPort2[i]; 69 | flag2 = 1; 70 | } 71 | } 72 | 73 | if (flag1) { 74 | Joystick[0].setYAxis(0); 75 | Joystick[0].setXAxis(0); 76 | if (!newStatusPort1[4]) Joystick[0].setYAxis(-127); //UP 77 | if (!newStatusPort1[5]) Joystick[0].setYAxis(127); //DOWN 78 | if (!newStatusPort1[6]) Joystick[0].setXAxis(-127); //LEFT 79 | if (!newStatusPort1[7]) Joystick[0].setXAxis(127); //RIGHT 80 | Joystick[0].setButton(0, !newStatusPort1[0]); //BUTTON1 (A) 81 | Joystick[0].setButton(1, !newStatusPort1[1]); //BUTTON2 (B) 82 | Joystick[0].setButton(2, !newStatusPort1[2]); //BUTTON3 (Select) 83 | Joystick[0].setButton(3, !newStatusPort1[3]); //BUTTON4 (Start) 84 | } 85 | 86 | if (flag2) { 87 | Joystick[1].setYAxis(0); 88 | Joystick[1].setXAxis(0); 89 | if (!newStatusPort2[4]) Joystick[1].setYAxis(-127); //UP 90 | if (!newStatusPort2[5]) Joystick[1].setYAxis(127); //DOWN 91 | if (!newStatusPort2[6]) Joystick[1].setXAxis(-127); //LEFT 92 | if (!newStatusPort2[7]) Joystick[1].setXAxis(127); //RIGHT 93 | Joystick[1].setButton(0, !newStatusPort2[0]); //BUTTON1 (A) 94 | Joystick[1].setButton(1, !newStatusPort2[1]); //BUTTON2 (B) 95 | Joystick[1].setButton(2, !newStatusPort2[2]); //BUTTON3 (Select) 96 | Joystick[1].setButton(3, !newStatusPort2[3]); //BUTTON4 (Start) 97 | } 98 | 99 | if (flag1) Joystick[0].sendState(); 100 | if (flag2) Joystick[1].sendState(); 101 | flag1 = 0; 102 | flag2 = 0; 103 | 104 | } 105 | 106 | #define latchlow digitalWrite(LATCH1, LOW); digitalWrite(LATCH2, LOW); 107 | #define latchhigh digitalWrite(LATCH1, HIGH); digitalWrite(LATCH2, HIGH) 108 | #define clocklow digitalWrite(CLOCK1, LOW); digitalWrite(CLOCK2, LOW) 109 | #define clockhigh digitalWrite(CLOCK1, HIGH); digitalWrite(CLOCK2, HIGH) 110 | #define wait delayMicroseconds(12) 111 | 112 | void ReadNESjoy() { 113 | latchlow; 114 | clocklow; 115 | latchhigh; 116 | wait; 117 | latchlow; 118 | 119 | for (int i = 0; i < 8; i++) { 120 | newStatusPort1[i] = digitalRead(DATA1); 121 | newStatusPort2[i] = digitalRead(DATA2); 122 | clockhigh; 123 | wait; 124 | clocklow; 125 | wait; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /test/RetroJoystickAdapter-DualShock.ino: -------------------------------------------------------------------------------- 1 | #include "Joystick.h" 2 | 3 | 4 | #define DATA 2 5 | #define CMD 3 6 | #define ATT 4 7 | #define CLK 5 8 | 9 | 10 | //#define DEBUG 11 | 12 | byte shift(byte dataOut, uint8_t readmode = 0) { // Does the actual shifting, both in and out 13 | boolean temp = 0; 14 | byte dataIn = 0; 15 | byte delay = 10; 16 | for (uint8_t i = 0; i <= 7; i++) { 17 | if ( dataOut & (1 << i) ) { 18 | digitalWrite(CMD, HIGH); // Writes out the _dataOut bits 19 | } else { 20 | digitalWrite(CMD, LOW); 21 | } 22 | digitalWrite(CLK, LOW); 23 | delayMicroseconds(delay); 24 | temp = digitalRead(DATA); // Reads the data pin 25 | if (temp) dataIn = dataIn | (B00000001 << i); 26 | digitalWrite(CLK, HIGH); 27 | delayMicroseconds(delay); 28 | } 29 | return dataIn; 30 | } 31 | 32 | uint8_t type; 33 | uint8_t ready; 34 | uint8_t btngrp1; 35 | uint8_t btngrp2; 36 | uint8_t analog1; 37 | uint8_t analog2; 38 | uint8_t analog3; 39 | uint8_t analog4; 40 | 41 | void debug() { 42 | Serial.print("d1: "); 43 | Serial.print(type,HEX); 44 | Serial.print(", "); 45 | Serial.print("d2: "); 46 | Serial.print(ready,HEX); 47 | Serial.print(", "); 48 | Serial.print("d3: "); 49 | Serial.print(btngrp1,HEX); 50 | Serial.print(", "); 51 | Serial.print("d4: "); 52 | Serial.print(btngrp2,HEX); 53 | Serial.print(", "); 54 | Serial.print("d5: "); 55 | Serial.print(analog1,HEX); 56 | Serial.print(", "); 57 | Serial.print("d6: "); 58 | Serial.print(analog2,HEX); 59 | Serial.print(", "); 60 | Serial.print("d7: "); 61 | Serial.print(analog3,HEX); 62 | Serial.print(", "); 63 | Serial.print("d8: "); 64 | Serial.println(analog4,HEX); 65 | } 66 | 67 | void readJoysticks() { 68 | 69 | digitalWrite(ATT, LOW); 70 | shift(0x01); 71 | type = shift(0x42); //type (0x41 / 0x73) 72 | ready = shift(0xFF); //ready (0x5a) 73 | btngrp1 = shift(0xFF); 74 | btngrp2 = shift(0xFF); 75 | analog1 = shift(0xFF); 76 | analog2 = shift(0xFF); 77 | analog3 = shift(0xFF); 78 | analog4 = shift(0xFF); 79 | digitalWrite(ATT, HIGH); 80 | 81 | delayMicroseconds(1000); 82 | #ifdef DEBUG 83 | debug(); 84 | delay(100); 85 | #endif 86 | } 87 | 88 | //btngrp1 89 | #define LEFT 0x80 90 | #define DOWN 0x40 91 | #define RIGHT 0x20 92 | #define UP 0x10 93 | #define START 0x08 94 | #define JOYR 0x04 95 | #define JOYL 0x02 96 | #define SELECT 0x01 97 | 98 | //btngrp2 99 | #define SQUARE 0x80 100 | #define X 0x40 101 | #define O 0x20 102 | #define TRIANGLE 0x10 103 | #define R1 0x08 104 | #define L1 0x04 105 | #define R2 0x02 106 | #define L2 0x01 107 | 108 | void interpretJoystickState() { 109 | //Joystick.setYAxis(0); 110 | //Joystick.setXAxis(0); 111 | 112 | Joystick.setXAxis( ((signed int)analog3)-127 ); 113 | Joystick.setYAxis( ((signed int)analog4)-127 ); 114 | Joystick.setXAxisRotation( (analog1*1.4) ); 115 | Joystick.setYAxisRotation( (255-analog2)*1.4 ); 116 | Joystick.setButton(0, !(btngrp1&START) ); //BUTTON1 (Start) 117 | Joystick.setButton(1, !(btngrp1&SELECT) ); //BUTTON2 (Select) 118 | Joystick.setButton(2, !(btngrp2&X) ); //BUTTON3 (A) 119 | Joystick.setButton(3, !(btngrp2&O) ); //BUTTON4 (B) 120 | Joystick.setButton(4, !(btngrp2&SQUARE) ); //BUTTON5 (X) 121 | Joystick.setButton(5, !(btngrp2&TRIANGLE) ); //BUTTON6 (Y) 122 | Joystick.setButton(6, !(btngrp2&L2) ); //BUTTON7 (LB) 123 | Joystick.setButton(7, !(btngrp2&R2) ); //BUTTON8 (RB) 124 | Joystick.setButton(8, !(btngrp2&L1) ); //BUTTON9 (LT) 125 | Joystick.setButton(9, !(btngrp2&R1) ); //BUTTON10 (RT) 126 | Joystick.setButton(10, !(btngrp1&JOYL) ); //BUTTON11 (Left Thumb) 127 | Joystick.setButton(11, !(btngrp1&JOYR) ); //BUTTON12 (Right Thumb) 128 | 129 | Joystick.setHatSwitch(0,-1); 130 | if ( !(btngrp1&UP) ) Joystick.setHatSwitch(0,0); //UP 131 | if ( !(btngrp1&DOWN) ) Joystick.setHatSwitch(0,180); //DOWN 132 | if ( !(btngrp1&LEFT) ) Joystick.setHatSwitch(0,270); //LEFT 133 | if ( !(btngrp1&RIGHT) ) Joystick.setHatSwitch(0,90); //RIGHT 134 | 135 | } 136 | 137 | 138 | 139 | 140 | 141 | void setup() { 142 | 143 | pinMode(DATA, INPUT_PULLUP); 144 | pinMode(CMD, OUTPUT); 145 | pinMode(ATT, OUTPUT); 146 | pinMode(CLK, OUTPUT); 147 | 148 | Joystick.begin(false); 149 | 150 | #ifdef DEBUG 151 | Serial.begin(9600); 152 | #endif 153 | 154 | } 155 | 156 | 157 | 158 | 159 | void loop() { 160 | 161 | readJoysticks(); 162 | interpretJoystickState(); 163 | Joystick.sendState(); 164 | 165 | } 166 | -------------------------------------------------------------------------------- /test/RetroJoystickAdapter-NES.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Description: Interfacing a NES controller with a PC with an Arduino. 3 | Coded by: Prodigity 4 | Date: 1 December 2011 5 | Revision: V0.93 (beta) 6 | Modified by: Matt Booth (20 December 2014) 7 | Adapted for USB (ATmega32U4, 5V) by Kurg 3.9.2016 8 | */ 9 | 10 | #include "Joystick2.h" 11 | 12 | uint8_t lastStatusPort1 = 0xff; 13 | uint8_t newStatusPort1 = 0xff; 14 | 15 | 16 | const int latch = 8; 17 | const int clock = 9; 18 | const int data = 7; 19 | 20 | #define latchlow digitalWrite(latch, LOW) 21 | #define latchhigh digitalWrite(latch, HIGH) 22 | #define clocklow digitalWrite(clock, LOW) 23 | #define clockhigh digitalWrite(clock, HIGH) 24 | #define dataread digitalRead(data) 25 | 26 | // http://www.mit.edu/~tarvizo/nes-controller.html 27 | #define wait delayMicroseconds(12) 28 | 29 | byte output; 30 | 31 | void setup() { 32 | 33 | // Serial.begin(9600); 34 | pinMode(latch, OUTPUT); 35 | pinMode(clock, OUTPUT); 36 | pinMode(data, INPUT); 37 | 38 | Joystick[0].begin(false); 39 | } 40 | 41 | void loop() { 42 | output = 0; 43 | ReadNESjoy(); 44 | //Serial.println(output,BIN); 45 | newStatusPort1 = output; 46 | if (lastStatusPort1 != newStatusPort1) { 47 | Joystick[0].setYAxis(0); 48 | Joystick[0].setXAxis(0); 49 | if (!bitRead(newStatusPort1,4)) Joystick[0].setYAxis(-127); //UP 50 | if (!bitRead(newStatusPort1,5)) Joystick[0].setYAxis(127); //DOWN 51 | if (!bitRead(newStatusPort1,6)) Joystick[0].setXAxis(-127); //LEFT 52 | if (!bitRead(newStatusPort1,7)) Joystick[0].setXAxis(127); //RIGHT 53 | Joystick[0].setButton(0, !bitRead(newStatusPort1,0)); //BUTTON1 (A) 54 | Joystick[0].setButton(1, !bitRead(newStatusPort1,1)); //BUTTON2 (B) 55 | Joystick[0].setButton(2, !bitRead(newStatusPort1,2)); //BUTTON3 (Select) 56 | Joystick[0].setButton(3, !bitRead(newStatusPort1,3)); //BUTTON4 (Start) 57 | Joystick[0].sendState(); 58 | lastStatusPort1 = newStatusPort1; 59 | } 60 | 61 | } 62 | 63 | 64 | void ReadNESjoy() { 65 | latchlow; 66 | clocklow; 67 | latchhigh; 68 | wait; 69 | latchlow; 70 | 71 | for (int i = 0; i < 8; i++) { 72 | output += dataread * (1 << i); 73 | clockhigh; 74 | wait; 75 | clocklow; 76 | wait; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/RetroJoystickAdapter-SegaGenesis.ino: -------------------------------------------------------------------------------- 1 | //Sega Megadrive/Genesis 6-button controller adapter by Kurg 2.9.2016 2 | //Tested with Arduino Pro Micro (ATmega32U4, 5V) from Ebay ($4) 3 | //Selected board from Arduino IDE: Arduino Leonardo 4 | //Joystick2 library from https://github.com/MHeironimus/ArduinoJoystickLibrary 5 | //https://www.cs.cmu.edu/~chuck/infopg/segasix.txt 6 | 7 | #include "Joystick2.h" 8 | 9 | //DB9 pin (9-pin D-connector) 10 | //Select low Select high 3rd pulse 11 | //1=Up Up Z 12 | //2=Down Down Y 13 | //3=Ground Left X 14 | //4=Ground Right 15 | //5=+5V 16 | //6=A B 17 | //7=Control("Select") 18 | //8=Ground 19 | //9=Start C 20 | 21 | #define EVENTS_TOTAL 4+6+1 //4 directions, 6 fire-buttons and Start 22 | #define INPUT_PINS_TOTAL 6 23 | 24 | //DB9 (port1): 1 2 3 4 6 9 25 | const uint8_t inputPinsPort1[] = {10, 16, 14, 15, 3, A1}; 26 | #define VCC_PORT1 A0 //DB9 (port1) pin 5 //comment out if not connected to IO-pin 27 | #define MODE_SELECT_PORT1 A3 //DB9 (port1) pin 7 28 | //DB9 (port 2) pin 8 = GND 29 | 30 | //DB9 (port2): 1 2 3 4 6 9 31 | const uint8_t inputPinsPort2[] = { 5, 6, 7, 8, 4, A2}; 32 | #define VCC_PORT2 9 //DB9 (port 2) pin 5 //comment out if not connected to IO-pin 33 | #define MODE_SELECT_PORT2 2 //DB9 (port2) pin 7 34 | //DB9 (port 2) pin 8 = GND 35 | 36 | uint8_t lastStatusPort1[EVENTS_TOTAL]; 37 | uint8_t newStatusPort1[EVENTS_TOTAL]; 38 | uint8_t lastStatusPort2[EVENTS_TOTAL]; 39 | uint8_t newStatusPort2[EVENTS_TOTAL]; 40 | 41 | 42 | void modeSelect(uint8_t m) { 43 | digitalWrite(MODE_SELECT_PORT1, m); 44 | digitalWrite(MODE_SELECT_PORT2, m); 45 | delayMicroseconds(20); 46 | } 47 | 48 | 49 | void setup() { 50 | 51 | //clear statusarrays (1=OFF, 0=ON) 52 | for (uint8_t i = 0; i < EVENTS_TOTAL; i++) { 53 | lastStatusPort1[i] = 1; 54 | newStatusPort1[i] = 1; 55 | lastStatusPort2[i] = 1; 56 | newStatusPort2[i] = 1; 57 | } 58 | 59 | #if defined(VCC_PORT1) && defined(VCC_PORT2) 60 | pinMode(VCC_PORT1, OUTPUT); 61 | pinMode(VCC_PORT2, OUTPUT); 62 | digitalWrite(VCC_PORT1, HIGH); 63 | digitalWrite(VCC_PORT2, HIGH); 64 | #endif 65 | 66 | for (int i=0; i < INPUT_PINS_TOTAL; i++) { 67 | pinMode(inputPinsPort1[i], INPUT_PULLUP); 68 | pinMode(inputPinsPort2[i], INPUT_PULLUP); 69 | } 70 | 71 | pinMode(MODE_SELECT_PORT1, OUTPUT); 72 | pinMode(MODE_SELECT_PORT2, OUTPUT); 73 | modeSelect(HIGH); 74 | 75 | Joystick[0].begin(false); 76 | Joystick[1].begin(false); 77 | 78 | } 79 | 80 | 81 | void read3buttons() { 82 | 83 | modeSelect(LOW); 84 | 85 | newStatusPort1[4] = digitalRead(inputPinsPort1[4]); //A1 86 | newStatusPort1[7] = digitalRead(inputPinsPort1[5]); //Start1 87 | newStatusPort2[4] = digitalRead(inputPinsPort2[4]); //A2 88 | newStatusPort2[7] = digitalRead(inputPinsPort2[5]); //Start2 89 | 90 | modeSelect(HIGH); 91 | 92 | for (uint8_t i=0; i < 4; i++) { 93 | newStatusPort1[i] = digitalRead(inputPinsPort1[i]); //AXES1 94 | newStatusPort2[i] = digitalRead(inputPinsPort2[i]); //AXES2 95 | } 96 | newStatusPort1[5] = digitalRead(inputPinsPort1[4]); //B1 97 | newStatusPort1[6] = digitalRead(inputPinsPort1[5]); //C1 98 | newStatusPort2[5] = digitalRead(inputPinsPort2[4]); //B2 99 | newStatusPort2[6] = digitalRead(inputPinsPort2[5]); //C2 100 | 101 | } 102 | 103 | 104 | uint8_t flag1 = 0; 105 | uint8_t flag2 = 0; 106 | 107 | void loop() { 108 | 109 | read3buttons(); 110 | 111 | //read X,Y,Z 112 | modeSelect(LOW); 113 | modeSelect(HIGH); 114 | modeSelect(LOW); 115 | modeSelect(HIGH); 116 | newStatusPort1[8] = digitalRead(inputPinsPort1[2]); //X1 117 | newStatusPort1[9] = digitalRead(inputPinsPort1[1]); //Y1 118 | newStatusPort1[10] = digitalRead(inputPinsPort1[0]); //Z1 119 | newStatusPort2[8] = digitalRead(inputPinsPort2[2]); //X2 120 | newStatusPort2[9] = digitalRead(inputPinsPort2[1]); //Y2 121 | newStatusPort2[10] = digitalRead(inputPinsPort2[0]); //Z2 122 | delayMicroseconds(1000); 123 | 124 | //check for changes - do not raise a flag if nothing changes 125 | for (uint8_t i=0; i < EVENTS_TOTAL; i++) { 126 | if (newStatusPort1[i] != lastStatusPort1[i]) { 127 | lastStatusPort1[i] = newStatusPort1[i]; 128 | flag1 = 1; 129 | } 130 | if (newStatusPort2[i] != lastStatusPort2[i]) { 131 | lastStatusPort2[i] = newStatusPort2[i]; 132 | flag2 = 1; 133 | } 134 | } 135 | 136 | if (flag1) { 137 | Joystick[0].setYAxis(0); 138 | Joystick[0].setXAxis(0); 139 | if (!newStatusPort1[0]) Joystick[0].setYAxis(-127); //UP 140 | if (!newStatusPort1[1]) Joystick[0].setYAxis(127); //DOWN 141 | if (!newStatusPort1[2]) Joystick[0].setXAxis(-127); //LEFT 142 | if (!newStatusPort1[3]) Joystick[0].setXAxis(127); //RIGHT 143 | Joystick[0].setButton(0, !newStatusPort1[4]); //BUTTON1 144 | Joystick[0].setButton(1, !newStatusPort1[5]); //BUTTON2 145 | Joystick[0].setButton(2, !newStatusPort1[6]); //BUTTON3 146 | Joystick[0].setButton(3, !newStatusPort1[7]); //BUTTON4 147 | Joystick[0].setButton(4, !newStatusPort1[8]); //BUTTON5 148 | Joystick[0].setButton(5, !newStatusPort1[9]); //BUTTON6 149 | Joystick[0].setButton(6, !newStatusPort1[10]); //BUTTON7 150 | Joystick[0].sendState(); 151 | flag1 = 0; 152 | } 153 | 154 | if (flag2) { 155 | Joystick[1].setYAxis(0); 156 | Joystick[1].setXAxis(0); 157 | if (!newStatusPort2[0]) Joystick[1].setYAxis(-127); //UP 158 | if (!newStatusPort2[1]) Joystick[1].setYAxis(127); //DOWN 159 | if (!newStatusPort2[2]) Joystick[1].setXAxis(-127); //LEFT 160 | if (!newStatusPort2[3]) Joystick[1].setXAxis(127); //RIGHT 161 | Joystick[1].setButton(0, !newStatusPort2[4]); //BUTTON1 162 | Joystick[1].setButton(1, !newStatusPort2[5]); //BUTTON2 163 | Joystick[1].setButton(2, !newStatusPort2[6]); //BUTTON3 164 | Joystick[1].setButton(3, !newStatusPort2[7]); //BUTTON4 165 | Joystick[1].setButton(4, !newStatusPort2[8]); //BUTTON5 166 | Joystick[1].setButton(5, !newStatusPort2[9]); //BUTTON6 167 | Joystick[1].setButton(6, !newStatusPort2[10]); //BUTTON7 168 | Joystick[1].sendState(); 169 | flag2 = 0; 170 | } 171 | 172 | 173 | } 174 | -------------------------------------------------------------------------------- /test/RetroJoystickAdapter-SegaGenesisKonami.ino: -------------------------------------------------------------------------------- 1 | //Sega Megadrive/Genesis 6-button controller adapter by Kurg 2.9.2016 2 | //Tested with Arduino Pro Micro (ATmega32U4, 5V) from Ebay ($4) 3 | //Selected board from Arduino IDE: Arduino Leonardo 4 | //Joystick2 library from https://github.com/MHeironimus/ArduinoJoystickLibrary 5 | //https://www.cs.cmu.edu/~chuck/infopg/segasix.txt 6 | 7 | #include "Joystick2.h" 8 | 9 | //DB9 pin (9-pin D-connector) 10 | //Select low Select high 3rd pulse 11 | //1=Up Up Z 12 | //2=Down Down Y 13 | //3=Ground Left X 14 | //4=Ground Right 15 | //5=+5V 16 | //6=A B 17 | //7=Control("Select") 18 | //8=Ground 19 | //9=Start C 20 | 21 | #define EVENTS_TOTAL 4+6+1 //4 directions, 6 fire-buttons and Start 22 | #define INPUT_PINS_TOTAL 6 23 | 24 | //DB9 (port1): 1 2 3 4 6 9 25 | const uint8_t inputPinsPort1[] = {10, 16, 14, 15, 3, A1}; 26 | #define VCC_PORT1 A0 //DB9 (port1) pin 5 //comment out if not connected to IO-pin 27 | #define MODE_SELECT_PORT1 A3 //DB9 (port1) pin 7 28 | //DB9 (port 2) pin 8 = GND 29 | 30 | //DB9 (port2): 1 2 3 4 6 9 31 | const uint8_t inputPinsPort2[] = { 5, 6, 7, 8, 4, A2}; 32 | #define VCC_PORT2 9 //DB9 (port 2) pin 5 //comment out if not connected to IO-pin 33 | #define MODE_SELECT_PORT2 2 //DB9 (port2) pin 7 34 | //DB9 (port 2) pin 8 = GND 35 | 36 | uint8_t lastStatusPort1[EVENTS_TOTAL]; 37 | uint8_t newStatusPort1[EVENTS_TOTAL]; 38 | uint8_t lastStatusPort2[EVENTS_TOTAL]; 39 | uint8_t newStatusPort2[EVENTS_TOTAL]; 40 | 41 | 42 | void modeSelect(uint8_t m) { 43 | digitalWrite(MODE_SELECT_PORT1, m); 44 | digitalWrite(MODE_SELECT_PORT2, m); 45 | delayMicroseconds(20); 46 | } 47 | 48 | void releaseAll(uint8_t j) { 49 | delay(20); 50 | Joystick[j].setButton(0, 0); //BUTTON1 51 | Joystick[j].setButton(1, 0); //BUTTON2 52 | Joystick[j].setButton(2, 0); //BUTTON3 53 | Joystick[j].setButton(3, 0); //BUTTON4 54 | Joystick[j].setButton(4, 0); //BUTTON5 55 | Joystick[j].setButton(5, 0); //BUTTON6 56 | Joystick[j].setButton(6, 0); //BUTTON7 57 | Joystick[j].setYAxis(0); 58 | Joystick[j].setXAxis(0); 59 | Joystick[j].sendState(); 60 | delay(20); 61 | } 62 | 63 | void KonamiCode(uint8_t j, uint8_t swap_ab = 0) { 64 | //https://en.wikipedia.org/wiki/Konami_Code 65 | //https://en.wikipedia.org/wiki/List_of_Konami_code_games 66 | //UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B, A 67 | releaseAll(j); 68 | delay(200); 69 | Joystick[j].setYAxis(-127); Joystick[j].sendState(); //UP 70 | releaseAll(j); 71 | Joystick[j].setYAxis(-127); Joystick[j].sendState(); //UP 72 | releaseAll(j); 73 | Joystick[j].setYAxis(127); Joystick[j].sendState(); //DOWN 74 | releaseAll(j); 75 | Joystick[j].setYAxis(127); Joystick[j].sendState(); //DOWN 76 | releaseAll(j); 77 | Joystick[j].setXAxis(-127); Joystick[j].sendState(); //LEFT 78 | releaseAll(j); 79 | Joystick[j].setXAxis(127); Joystick[j].sendState(); //RIGHT 80 | releaseAll(j); 81 | Joystick[j].setXAxis(-127); Joystick[j].sendState(); //LEFT 82 | releaseAll(j); 83 | Joystick[j].setXAxis(127); Joystick[j].sendState(); //RIGHT 84 | releaseAll(j); 85 | if (swap_ab) { 86 | Joystick[j].setButton(0, 1); Joystick[j].sendState(); //BUTTON1 (A) 87 | releaseAll(j); 88 | Joystick[j].setButton(1, 1); Joystick[j].sendState(); //BUTTON2 (B) 89 | releaseAll(j); 90 | } else { 91 | Joystick[j].setButton(1, 1); Joystick[j].sendState(); //BUTTON2 (B) 92 | releaseAll(j); 93 | Joystick[j].setButton(0, 1); Joystick[j].sendState(); //BUTTON1 (A) 94 | releaseAll(j); 95 | } 96 | delay(100); 97 | } 98 | 99 | 100 | void setup() { 101 | 102 | //clear statusarrays (1=OFF, 0=ON) 103 | for (uint8_t i = 0; i < EVENTS_TOTAL; i++) { 104 | lastStatusPort1[i] = 1; 105 | newStatusPort1[i] = 1; 106 | lastStatusPort2[i] = 1; 107 | newStatusPort2[i] = 1; 108 | } 109 | 110 | #if defined(VCC_PORT1) && defined(VCC_PORT2) 111 | pinMode(VCC_PORT1, OUTPUT); 112 | pinMode(VCC_PORT2, OUTPUT); 113 | digitalWrite(VCC_PORT1, HIGH); 114 | digitalWrite(VCC_PORT2, HIGH); 115 | #endif 116 | 117 | for (int i=0; i < INPUT_PINS_TOTAL; i++) { 118 | pinMode(inputPinsPort1[i], INPUT_PULLUP); 119 | pinMode(inputPinsPort2[i], INPUT_PULLUP); 120 | } 121 | 122 | pinMode(MODE_SELECT_PORT1, OUTPUT); 123 | pinMode(MODE_SELECT_PORT2, OUTPUT); 124 | modeSelect(HIGH); 125 | 126 | Joystick[0].begin(false); 127 | Joystick[1].begin(false); 128 | 129 | } 130 | 131 | 132 | void read3buttons() { 133 | 134 | modeSelect(LOW); 135 | 136 | newStatusPort1[4] = digitalRead(inputPinsPort1[4]); //A1 137 | newStatusPort1[7] = digitalRead(inputPinsPort1[5]); //Start1 138 | newStatusPort2[4] = digitalRead(inputPinsPort2[4]); //A2 139 | newStatusPort2[7] = digitalRead(inputPinsPort2[5]); //Start2 140 | 141 | modeSelect(HIGH); 142 | 143 | for (uint8_t i=0; i < 4; i++) { 144 | newStatusPort1[i] = digitalRead(inputPinsPort1[i]); //AXES1 145 | newStatusPort2[i] = digitalRead(inputPinsPort2[i]); //AXES2 146 | } 147 | newStatusPort1[5] = digitalRead(inputPinsPort1[4]); //B1 148 | newStatusPort1[6] = digitalRead(inputPinsPort1[5]); //C1 149 | newStatusPort2[5] = digitalRead(inputPinsPort2[4]); //B2 150 | newStatusPort2[6] = digitalRead(inputPinsPort2[5]); //C2 151 | 152 | } 153 | 154 | 155 | uint8_t flag1 = 0; 156 | uint8_t flag2 = 0; 157 | 158 | void loop() { 159 | 160 | read3buttons(); 161 | 162 | //read X,Y,Z 163 | modeSelect(LOW); 164 | modeSelect(HIGH); 165 | modeSelect(LOW); 166 | modeSelect(HIGH); 167 | newStatusPort1[8] = digitalRead(inputPinsPort1[2]); //X1 168 | newStatusPort1[9] = digitalRead(inputPinsPort1[1]); //Y1 169 | newStatusPort1[10] = digitalRead(inputPinsPort1[0]); //Z1 170 | newStatusPort2[8] = digitalRead(inputPinsPort2[2]); //X2 171 | newStatusPort2[9] = digitalRead(inputPinsPort2[1]); //Y2 172 | newStatusPort2[10] = digitalRead(inputPinsPort2[0]); //Z2 173 | delayMicroseconds(1000); 174 | 175 | //check for changes - do not raise a flag if nothing changes 176 | for (uint8_t i=0; i < EVENTS_TOTAL; i++) { 177 | if (newStatusPort1[i] != lastStatusPort1[i]) { 178 | lastStatusPort1[i] = newStatusPort1[i]; 179 | flag1 = 1; 180 | } 181 | if (newStatusPort2[i] != lastStatusPort2[i]) { 182 | lastStatusPort2[i] = newStatusPort2[i]; 183 | flag2 = 1; 184 | } 185 | } 186 | 187 | if (flag1) { 188 | Joystick[0].setYAxis(0); 189 | Joystick[0].setXAxis(0); 190 | if (!newStatusPort1[0]) Joystick[0].setYAxis(-127); //UP 191 | if (!newStatusPort1[1]) Joystick[0].setYAxis(127); //DOWN 192 | if (!newStatusPort1[2]) Joystick[0].setXAxis(-127); //LEFT 193 | if (!newStatusPort1[3]) Joystick[0].setXAxis(127); //RIGHT 194 | Joystick[0].setButton(0, !newStatusPort1[4]); //BUTTON1 195 | Joystick[0].setButton(1, !newStatusPort1[5]); //BUTTON2 196 | Joystick[0].setButton(2, !newStatusPort1[6]); //BUTTON3 197 | Joystick[0].setButton(3, !newStatusPort1[7]); //BUTTON4 198 | Joystick[0].setButton(4, !newStatusPort1[8]); //BUTTON5 199 | Joystick[0].setButton(5, !newStatusPort1[9]); //BUTTON6 200 | Joystick[0].setButton(6, !newStatusPort1[10]); //BUTTON7 201 | Joystick[0].sendState(); 202 | flag1 = 0; 203 | if (!newStatusPort1[4] && !newStatusPort1[5] && !newStatusPort1[6] && !newStatusPort1[0]) KonamiCode(0,0); 204 | if (!newStatusPort1[4] && !newStatusPort1[5] && !newStatusPort1[6] && !newStatusPort1[1]) KonamiCode(0,1); 205 | } 206 | 207 | if (flag2) { 208 | Joystick[1].setYAxis(0); 209 | Joystick[1].setXAxis(0); 210 | if (!newStatusPort2[0]) Joystick[1].setYAxis(-127); //UP 211 | if (!newStatusPort2[1]) Joystick[1].setYAxis(127); //DOWN 212 | if (!newStatusPort2[2]) Joystick[1].setXAxis(-127); //LEFT 213 | if (!newStatusPort2[3]) Joystick[1].setXAxis(127); //RIGHT 214 | Joystick[1].setButton(0, !newStatusPort2[4]); //BUTTON1 215 | Joystick[1].setButton(1, !newStatusPort2[5]); //BUTTON2 216 | Joystick[1].setButton(2, !newStatusPort2[6]); //BUTTON3 217 | Joystick[1].setButton(3, !newStatusPort2[7]); //BUTTON4 218 | Joystick[1].setButton(4, !newStatusPort2[8]); //BUTTON5 219 | Joystick[1].setButton(5, !newStatusPort2[9]); //BUTTON6 220 | Joystick[1].setButton(6, !newStatusPort2[10]); //BUTTON7 221 | Joystick[1].sendState(); 222 | flag2 = 0; 223 | if (!newStatusPort2[4] && !newStatusPort2[5] && !newStatusPort2[6] && !newStatusPort2[0]) KonamiCode(0,0); 224 | if (!newStatusPort2[4] && !newStatusPort2[5] && !newStatusPort2[6] && !newStatusPort2[1]) KonamiCode(0,1); 225 | } 226 | 227 | 228 | } 229 | --------------------------------------------------------------------------------