├── 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 | 
65 |
66 | #### USB Host Shield
67 | 
68 |
69 | #### Level converter with regulator
70 | 
71 |
72 | #### USB-shield pinout
73 | 
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 | 
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 | 
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 | 
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 | 
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 ) | MISO → | D12 (PB4) | D14 (PB3) | Use 1k pullup resistor to 3.3V!
30 | 2 CMD (orange ) | MOSI ← | D11 (PB3) | D16 (PB2) |
31 | 3 Vibration power (7.2-9V) (grey ) | | | |
32 | 4 GND (black ) | | GND | GND |
33 | 5 VCC (3.3V) (red ) | | VCC | VCC |
34 | 6 ATT (yellow ) | SS ← | D10 (PB2) | D10 (PB6) | (pro micro: SS would be D17 (PB0/RX_LED), but there is no pin D17)
35 | 7 CLK (blue ) | SCK ← | D13 (PB5) | D15 (PB1) |
36 | 8 unknown (white ) | | | |
37 | 9 ACK (green ) | | | | (when this is needed?)
38 | **Wii** | | | | |
39 | 1 VCC (3.3V) (red ) | | | |
40 | 2 SCL (yellow ) | | A5 (PC5) | D3 (PD0) |
41 | 3 Detect device (3.3V) (black ) | | | D4 (PD4) | Use 10k pulldown resistor to GND!
42 | 4 NC | | | |
43 | 5 SDA (green ) | | A4 (PC4) | D2 (PD1) |
44 | 6 GND (white ) | | | |
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 | 
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 | 
15 |
16 | ## Final product
17 |
18 | Here are Adriano's examples of his assembly.
19 |
20 | 
21 | 
22 | 
23 |
24 | Here are the Jarno's examples of his assembly.
25 |
26 | 
27 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------