├── N64_controller_replacement.ino
├── README.md
├── crc_table.h
├── encoder plug.png
├── mini_n64.png
├── n64_controller_replacement_memory.ino
├── n64_power.png
├── nano_n64.png
├── rumble_all.png
├── uno_n64.png
└── uno_n64r.png
/N64_controller_replacement.ino:
--------------------------------------------------------------------------------
1 | /**
2 | * Gamecube controller to Nintendo 64 adapter
3 | * by Andrew Brown
4 | * https://github.com/brownan/Gamecube-N64-Controller/blob/master/gamecube.ino
5 | *
6 | *
7 | * Rewritten by jtryba so the atmega328 can be used as a stand alone replacement to the Nintendo CNT-NUS N64 controller chip, with built in rumble, for use in portables.
8 | * N64 controller protocol can be found here:
9 | * https://code.google.com/archive/p/micro-64-controller/wikis/Protocol.wiki
10 | */
11 |
12 | /**
13 | * To use, hook up the following to the Arduino Atmega328
14 | * Digital I/O 8: N64 serial line
15 | * Digital I/O 10: 220 ohm resistor to 2n2222 base, emitter to ground, collector to motor using 1N4007 flyback diode accross motor legs, and finally motor to 5v
16 | * All appropriate grounding and power lines, joystick, and all buttons (all active low, see pin definitions below)
17 | *
18 | * /------------\
19 | * / O O O \
20 | * | GND Signl 3.3V |
21 | * |________________|
22 | * (Front of N64)
23 | *
24 | * The pin-out for the N64 and Gamecube wires can be found here:
25 | * http://svn.navi.cx/misc/trunk/wasabi/devices/cube64/hardware/cube64-basic.pdf
26 | * Note: that diagram is not for this project, but for a similar project which
27 | * uses a PIC microcontroller. However, the diagram does describe the pinouts
28 | * of the gamecube and N64 wires.
29 | *
30 | * Also note: the N64 supplies a 3.3 volt line, but I don't plug that into
31 | * anything. The arduino can't run off of that many volts, it needs more, so
32 | * it's powered externally. Therefore, only two lines
33 | * from the N64 are used.
34 | *
35 | * Just use the 5v rail to power this in a portable.
36 | */
37 |
38 | #if defined(ARDUINO_AVR_MINI)
39 | #define BOARD "Mini"
40 | #elif defined(ARDUINO_AVR_NANO)
41 | #define BOARD "Nano"
42 | #elif defined(ARDUINO_AVR_PRO)
43 | #define BOARD "Pro"
44 | #elif defined(ARDUINO_AVR_UNO)
45 | #define BOARD "Uno"
46 | #elif defined(ARDUINO_AVR_MICRO)
47 | #define BOARD "Micro"
48 | #else
49 | #error "Unsupported board"
50 | #endif
51 |
52 | //#define DEBUG
53 | //#define DEBUG_VERBOSE
54 | //#define USE_ENCODER 1 // uncomment this line to use the orig n64 encoder, only nano supports this
55 |
56 | #ifdef DEBUG_VERBOSE
57 | #ifndef DEBUG
58 | #define DEBUG
59 | #endif
60 | #endif
61 |
62 | #include "pins_arduino.h"
63 |
64 | #define N64_PIN 8
65 | #define RUMBLE_PIN 10
66 | #define N64_HIGH DDRB &= ~0x01
67 | #define N64_LOW DDRB |= 0x01
68 | #define N64_QUERY (PINB & 0x01)
69 |
70 | // were using a PWM signal to control the rumble motor in order to save battery
71 | #define RUMBLE_FORCE 250 //0-255
72 |
73 | #define BUTTON_COUNT 14
74 | #define BTN_A 0 // RX0
75 | #define BTN_B 1 // TX1
76 | #define BTN_C_UP 2 // D2
77 | #define BTN_C_DOWN 3 // D3
78 | #define BTN_C_LEFT 4 // D4
79 | #define BTN_C_RIGHT 5 // D5
80 | #define BTN_L 9 // D9
81 | #define BTN_R A0 // A0
82 | #define BTN_Z 11 // D11
83 | #define PAD_UP 6 // D6
84 | #define PAD_DOWN 7 // D7
85 | #define PAD_LEFT 12 // D12
86 | #define PAD_RIGHT A3 // A3
87 | #define BTN_START 13 // D13
88 |
89 | // Control sticks:
90 | // 64 expects a signed value from -128 to 128 with 0 being neutral
91 | //
92 | // Additionally, the 64 controllers are relative. Whatever stick position
93 | // it's in when it's powered on is what it reports as 0.
94 | //
95 | // While the joystick data is a signed 8 bit 2s complement we know from Micro
96 | // that controllers only have 160 steps on them and I've had games which screw
97 | // up when given the full 8 bit range.
98 | //
99 | // 160 steps diveded by 2 is 80 steps in each direction, adding a buffer i used 90
100 | // also by using the controller_test.rom on official hardware, ive found that most controllers report closer to 90-100 anyway
101 | //
102 | // More technical info on joysticks can be found here:
103 | // http://n64devkit.square7.ch/pro-man/pro26/26-02.htm#01
104 | //
105 | #define JOY_MAX_REPORT 100 // 127 for full 8 bit data
106 |
107 | // Board: Mini/Uno // Nano
108 | #ifdef USE_ENCODER
109 |
110 | #if defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_PRO) || defined(ARDUINO_AVR_MICRO)
111 | #error("This is board does not support n64 encoder, not enough pins!")
112 | #elif defined(ARDUINO_AVR_NANO)
113 | #define encoderIx A1 // 1
114 | #define encoderQx A2 // 4
115 | #define encoderIy A6 // 5
116 | #define encoderQy A7 // 6 (white)
117 | #elif defined(ARDUINO_AVR_UNO)
118 | #define encoderIx A1 // 1
119 | #define encoderQx A2 // 4
120 | #define encoderIy A4 // 5
121 | #define encoderQy A5 // 6 (white)
122 | #else
123 | #error("This is board does not support the encoder! Not enough pins, must use Arduino Nano.")
124 | #endif
125 |
126 | volatile signed int countx;
127 | volatile signed int county;
128 | void handleEncoderX(void);
129 | void handleEncoderX(void);
130 | #else
131 | #define JOY_DEAD 2
132 | #define JOY_RANGE 400 // 1023 * 0.4 rounded a bit
133 | #define JOY_X A1
134 | #define JOY_Y A2
135 | //#define I2C_SCL A4
136 | //#define I2C_SDA A5
137 | #endif
138 |
139 | int JOY_X_MIN = 0; // will be calculated later in CalStick()
140 | int JOY_X_MAX = 1023; // will be calculated later in CalStick()
141 | int JOY_Y_MIN = 0; // will be calculated later in CalStick()
142 | int JOY_Y_MAX = 1023; // will be calculated later in CalStick()
143 |
144 | // Zero points for the controller stick
145 | static unsigned char zero_x;
146 | static unsigned char zero_y;
147 |
148 | const int btn[BUTTON_COUNT] =
149 | {
150 | BTN_A, BTN_B, BTN_Z, BTN_START, PAD_UP, PAD_DOWN, PAD_LEFT, PAD_RIGHT,
151 | BTN_L, BTN_R, BTN_C_UP, BTN_C_DOWN, BTN_C_LEFT, BTN_C_RIGHT
152 | };
153 |
154 | static char n64_raw_dump[281]; // maximum recv is 1+2+32 bytes + 1 bit
155 | // n64_raw_dump does /not/ include the command byte. That gets pushed into
156 | // n64_command:
157 | static unsigned char n64_command;
158 |
159 | // bytes to send to the 64
160 | // maximum we'll need to send is 33, 32 for a read request and 1 CRC byte
161 | static unsigned char n64_buffer[33];
162 |
163 | static void get_n64_command(void);
164 | static void n64_send(unsigned char *buffer, char length, bool wide_stop);
165 | static void n64_send_raw(unsigned char *buffer, char length);
166 | void ReadInputs(void);
167 | void CalStick(void);
168 | signed int GetStick_x(void);
169 | signed int GetStick_y(void);
170 |
171 | static bool rumble = false;
172 | static bool rumblelast = false;
173 | static unsigned char enableRumble = 0x01;
174 | static long lastSwitch = millis();
175 |
176 | #include "crc_table.h"
177 |
178 | void setup()
179 | {
180 | // Communication with the N64 on this pin
181 | digitalWrite(N64_PIN, LOW);
182 | pinMode(N64_PIN, INPUT);
183 |
184 | #ifdef DEBUG
185 | Serial.begin(115200);
186 | Serial.println("------------------");
187 | Serial.println("Setup has started!");
188 | #endif
189 |
190 | // setup I/O
191 | // buttons
192 | for (int i = 0; i < BUTTON_COUNT; i ++)
193 | {
194 | #ifdef DEBUG
195 | if (btn[i] == 0 || btn[i] == 1)
196 | {
197 | continue;
198 | }
199 | #endif
200 | digitalWrite(btn[i], LOW);
201 | pinMode(btn[i], INPUT_PULLUP);
202 | }
203 |
204 | // stick
205 | #ifdef USE_ENCODER
206 | countx=0;
207 | county=0;
208 | pinMode(encoderIx, INPUT);
209 | pinMode(encoderQx, INPUT);
210 | pinMode(encoderIy, INPUT);
211 | pinMode(encoderQy, INPUT);
212 | attachInterrupt(digitalPinToInterrupt(encoderIx), handleEncoderX, CHANGE);
213 | attachInterrupt(digitalPinToInterrupt(encoderIy), handleEncoderY, CHANGE);
214 | #else
215 | pinMode(JOY_X, INPUT);
216 | pinMode(JOY_Y, INPUT);
217 | #endif
218 |
219 | // rumble
220 | digitalWrite(RUMBLE_PIN, LOW);
221 | pinMode(RUMBLE_PIN, OUTPUT);
222 |
223 | #ifndef USE_ENCODER
224 | CalStick();
225 | #endif
226 |
227 | #ifdef DEBUG
228 | Serial.println("Code has started!");
229 | #endif
230 | }
231 |
232 | void ReadInputs(void)
233 | {
234 | // clear it out
235 | memset(n64_buffer, 0, sizeof(n64_buffer));
236 |
237 | // buttons
238 | // First byte in n64_buffer should contain:
239 | // A, B, Z, Start, Dup, Ddown, Dleft, Dright
240 | bitWrite(n64_buffer[0], 7, !digitalRead(btn[0]));
241 | bitWrite(n64_buffer[0], 6, !digitalRead(btn[1]));
242 | bitWrite(n64_buffer[0], 5, !digitalRead(btn[2]));
243 | bitWrite(n64_buffer[0], 4, !digitalRead(btn[3]));
244 | bitWrite(n64_buffer[0], 3, !digitalRead(btn[4]));
245 | bitWrite(n64_buffer[0], 2, !digitalRead(btn[5]));
246 | bitWrite(n64_buffer[0], 1, !digitalRead(btn[6]));
247 | bitWrite(n64_buffer[0], 0, !digitalRead(btn[7]));
248 |
249 | // Second byte to N64 should contain:
250 | // Reset, 0, L, R, Cup, Cdown, Cleft, Cright
251 | bitWrite(n64_buffer[1], 7, 0);
252 | bitWrite(n64_buffer[1], 6, 0);
253 | bitWrite(n64_buffer[1], 5, !digitalRead(btn[8]));
254 | bitWrite(n64_buffer[1], 4, !digitalRead(btn[9]));
255 | bitWrite(n64_buffer[1], 3, !digitalRead(btn[10]));
256 | bitWrite(n64_buffer[1], 2, !digitalRead(btn[11]));
257 | bitWrite(n64_buffer[1], 1, !digitalRead(btn[12]));
258 | bitWrite(n64_buffer[1], 0, !digitalRead(btn[13]));
259 |
260 | // user reset the controller
261 | byte l = bitRead(n64_buffer[1], 5);
262 | byte r = bitRead(n64_buffer[1], 4);
263 | byte start = bitRead(n64_buffer[0], 4);
264 | byte z = bitRead(n64_buffer[0], 5);
265 | if (l != 0 && r != 0)
266 | {
267 | if (start != 0)
268 | {
269 | //Serial.println("User reset the controller");
270 | analogWrite(RUMBLE_PIN, RUMBLE_FORCE);
271 | delay(50);
272 | analogWrite(RUMBLE_PIN, 0);
273 | rumble = false;
274 | bitWrite(n64_buffer[1], 7, 1); // set controller reset flag
275 | bitWrite(n64_buffer[0], 4, 0); // ignore start press
276 | CalStick();
277 | }
278 | if (z != 0)
279 | {
280 | bitWrite(n64_buffer[0], 5, 0); // ignore z press
281 | long elapsed = millis() - lastSwitch;
282 | if (elapsed > 100)
283 | {
284 | //Serial.println("User toggled rumble");
285 | analogWrite(RUMBLE_PIN, RUMBLE_FORCE);
286 | delay(50);
287 | analogWrite(RUMBLE_PIN, 0);
288 | rumble = false;
289 | enableRumble++;
290 | if (enableRumble > 0x01)
291 | {
292 | enableRumble = 0x00;
293 | analogWrite(RUMBLE_PIN, RUMBLE_FORCE);
294 | delay(50);
295 | analogWrite(RUMBLE_PIN, 0);
296 | }
297 | lastSwitch = millis();
298 | identify();
299 | }
300 | }
301 | }
302 |
303 | // Third byte: Control Stick X position
304 | n64_buffer[2] = -zero_x + GetStick_x();
305 | // Fourth byte: Control Stick Y Position
306 | n64_buffer[3] = -zero_y + GetStick_y();
307 |
308 | #ifdef DEBUG_PAD_DATA
309 | char buf[32];
310 | memset(buf, 0, 32);
311 | sprintf(buf, "0x%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
312 | bitRead(n64_buffer[0], 7),
313 | bitRead(n64_buffer[0], 6),
314 | bitRead(n64_buffer[0], 5),
315 | bitRead(n64_buffer[0], 4),
316 | bitRead(n64_buffer[0], 3),
317 | bitRead(n64_buffer[0], 2),
318 | bitRead(n64_buffer[0], 1),
319 | bitRead(n64_buffer[0], 0),
320 | bitRead(n64_buffer[1], 5),
321 | bitRead(n64_buffer[1], 4),
322 | bitRead(n64_buffer[1], 3),
323 | bitRead(n64_buffer[1], 2),
324 | bitRead(n64_buffer[1], 1),
325 | bitRead(n64_buffer[1], 0)
326 | );
327 | Serial.print(buf);
328 | Serial.print(" ");
329 | Serial.print(n64_buffer[2], HEX);
330 | Serial.print(" ");
331 | Serial.print(n64_buffer[3], HEX);
332 | Serial.print(" ");
333 | Serial.println(rumble, HEX);
334 | #endif
335 | }
336 |
337 | void CalStick(void)
338 | {
339 | #ifdef DEBUG
340 | Serial.println("Calibrating analog stick...");
341 | #endif
342 | #ifdef USE_ENCODER
343 | zero_x = 0;
344 | zero_y = 0;
345 | #else
346 | int t = 5;
347 | int x = 0;
348 | int y = 0;
349 | for (int i = 0; i < t; i++)
350 | {
351 | x += analogRead(JOY_X);
352 | y += analogRead(JOY_Y);
353 | }
354 |
355 | int center_x = x/t;
356 | int center_y = y/t;
357 |
358 | JOY_X_MIN = constrain(center_x-JOY_RANGE, 0, 1023);
359 | JOY_X_MAX = constrain(center_x+JOY_RANGE, 0, 1023);
360 | JOY_Y_MIN = constrain(center_y-JOY_RANGE, 0, 1023);
361 | JOY_Y_MAX = constrain(center_y+JOY_RANGE, 0, 1023);
362 |
363 | zero_x = GetStick_x();
364 | zero_y = GetStick_y();
365 | #endif
366 | #ifdef DEBUG
367 | Serial.print("Center x: ");
368 | Serial.println(center_x);
369 | Serial.print("Center y: ");
370 | Serial.println(center_y);
371 | Serial.println("Calibration complete!");
372 | #endif
373 | }
374 |
375 | #ifdef USE_ENCODER
376 |
377 | void handleEncoderX()
378 | {
379 | if(digitalRead(encoderIx) == digitalRead(encoderQx))
380 | {
381 | countx++;
382 | }
383 | else
384 | {
385 | countx--;
386 | }
387 | countx = constrain(countx, -JOY_MAX_REPORT, JOY_MAX_REPORT);
388 | }
389 |
390 | void handleEncoderY()
391 | {
392 | if(digitalRead(encoderIy) == digitalRead(encoderQy))
393 | {
394 | county++;
395 | }
396 | else
397 | {
398 | county--;
399 | }
400 | county = constrain(county, -JOY_MAX_REPORT, JOY_MAX_REPORT);
401 | }
402 |
403 | #endif
404 |
405 | signed int GetStick_x(void)
406 | {
407 | #ifdef USE_ENCODER
408 | return countx;
409 | #else
410 | unsigned int l = analogRead(JOY_X);
411 | l = constrain(l, JOY_X_MIN, JOY_X_MAX);
412 | signed int i = map(l, JOY_X_MIN, JOY_X_MAX, -JOY_MAX_REPORT, JOY_MAX_REPORT);
413 | if (i < JOY_DEAD && i > -JOY_DEAD)
414 | return 0;
415 | return i;
416 | #endif
417 | }
418 |
419 | signed int GetStick_y(void)
420 | {
421 | #ifdef USE_ENCODER
422 | return county;
423 | #else
424 | unsigned int l = analogRead(JOY_Y);
425 | l = constrain(l, JOY_Y_MIN, JOY_Y_MAX);
426 | signed int i = map(l, JOY_Y_MIN, JOY_Y_MAX, -JOY_MAX_REPORT, JOY_MAX_REPORT);
427 | if (i < JOY_DEAD && i > -JOY_DEAD)
428 | return 0;
429 | return i;
430 | #endif
431 | }
432 |
433 | void identify()
434 | {
435 | rumble = false;
436 |
437 | // mutilate the n64_buffer array with our status
438 | // we return 0x050001 to indicate we have a rumble pack
439 | // or 0x050002 to indicate the expansion slot is empty
440 | //
441 | // 0xFF I've seen sent from Mario 64 and Shadows of the Empire.
442 | // I don't know why it's different, but the controllers seem to
443 | // send a set of status bytes afterwards the same as 0x00, and
444 | // it won't work without it.
445 |
446 | n64_buffer[0] = 0x05;
447 | n64_buffer[1] = 0x00;
448 | n64_buffer[2] = enableRumble; // = 0x01
449 |
450 | // 000001010000000000000001b
451 |
452 | uint8_t oldSREG = SREG;
453 | // Clear interrupts
454 | cli();
455 | n64_send(n64_buffer, 3, 0);
456 | // Restore old interrupt state
457 | SREG = oldSREG;
458 | }
459 |
460 | void loop()
461 | {
462 | int status;
463 | unsigned char data, addr;
464 |
465 | ReadInputs();
466 |
467 | // Wait for incomming 64 command
468 | // this will block until the N64 sends us a command
469 | noInterrupts();
470 | get_n64_command();
471 |
472 | // 0x00 is identify command
473 | // 0x01 is status
474 | // 0x02 is read
475 | // 0x03 is write
476 | // 0xFF is reset and identify
477 | //
478 | // More info on reading and writing can be found here:
479 | // http://ultra64.ca/files/documentation/online-manuals/man/n64man/misc/glossarySystem.html#:~:text=Each%20256%20bytes%20is%20called,system%20for%20game%20note%20management.
480 | // and here:
481 | // http://n64devkit.square7.ch/pro-man/pro26/26-03.htm
482 | //
483 | switch (n64_command)
484 | {
485 | // identify command(s)
486 | case 0x7F: // required to make pokemon puzzle league recognise micro
487 | case 0xFD: // required to make sm64 recognise micro
488 | case 0xFE:
489 | case 0xFF:
490 | //CalStick();
491 | case 0x00:
492 | identify();
493 | break;
494 | // query command
495 | case 0x01:
496 | // blast out the pre-assembled array in n64_buffer
497 | n64_send(n64_buffer, 4, 0);
498 | break;
499 | // read command
500 | case 0x02:
501 | // Addresses 8000-8FFF is used to query the enable state. 1 = enabled, 0 = disabled.
502 |
503 | // A read. If the address is 0x8000, return 32 bytes of 0x80 bytes,
504 | // and a CRC byte. this tells the system our attached controller
505 | // pack is a rumble pack
506 |
507 | // Assume it's a read for 0x8000, which is the only thing it should
508 | // be requesting anyways
509 | memset(n64_buffer, (enableRumble == 0x01) ? 0x80 : 0x00, 32);
510 | n64_buffer[32] = 0xB8; // CRC 10111000b
511 | n64_send(n64_buffer, 33, 1);
512 | break;
513 | // write command
514 | case 0x03:
515 | // A write. we at least need to respond with a single CRC byte. If
516 | // the write was to address 0xC000 and the data was 0x01, turn on
517 | // rumble! All other write addresses are ignored. (but we still
518 | // need to return a CRC)
519 |
520 | // decode the first data byte (fourth overall byte), bits indexed
521 | // at 24 through 31
522 | data = 0;
523 | data |= (n64_raw_dump[16] != 0) << 7;
524 | data |= (n64_raw_dump[17] != 0) << 6;
525 | data |= (n64_raw_dump[18] != 0) << 5;
526 | data |= (n64_raw_dump[19] != 0) << 4;
527 | data |= (n64_raw_dump[20] != 0) << 3;
528 | data |= (n64_raw_dump[21] != 0) << 2;
529 | data |= (n64_raw_dump[22] != 0) << 1;
530 | data |= (n64_raw_dump[23] != 0);
531 |
532 | // decode the first half of the address, bits
533 | // 8 through 15
534 | addr = 0;
535 | addr |= (n64_raw_dump[0] != 0) << 7;
536 | addr |= (n64_raw_dump[1] != 0) << 6;
537 | addr |= (n64_raw_dump[2] != 0) << 5;
538 | addr |= (n64_raw_dump[3] != 0) << 4;
539 | addr |= (n64_raw_dump[4] != 0) << 3;
540 | addr |= (n64_raw_dump[5] != 0) << 2;
541 | addr |= (n64_raw_dump[6] != 0) << 1;
542 | addr |= (n64_raw_dump[7] != 0);
543 |
544 | if (addr == 0x80)
545 | {
546 | /*
547 | N64 sends: 03 80 01 followed by 32 bytes, all FE
548 | Response: 0xE1 with memory pak, 0x1E without.
549 | The response has no stop bit! instead, the data line
550 | goes low for 2us immediately after the last data bit. <- isnt that a long stop bit?
551 | N64 sends: 02 80 01
552 | Response: 32 0x80s with rumble pack, 32 0x00s without.
553 | Followed by CRC and no stop bit.
554 | */
555 | n64_buffer[1] = (enableRumble == 0x01) ? 0xE1 : 0x1E;
556 | n64_buffer[0] = crc_repeating_table[data] ^ 0xFF;
557 | n64_send_raw(n64_buffer, 2);
558 | }
559 | else
560 | {
561 | // get crc byte, invert it, as per the protocol for
562 | // having a memory card attached
563 | n64_buffer[0] = crc_repeating_table[data] ^ 0xFF;
564 | // send it
565 | n64_send(n64_buffer, 1, 1);
566 | }
567 | // end of time critical code
568 |
569 | // was the address the rumble latch at 0xC000?
570 | if (addr == 0xC0)
571 | {
572 | //Rumble pak writes:
573 | //To switch on the rumble pak motor, the N64 sends:
574 | //03 C0 1B 01 01 01 ...
575 | //This writes 01 to addresses starting at 0x4000.
576 |
577 | //To turn the motor back off, the N64 sends:
578 | //03 C0 1B 00 00 00 ...
579 | rumble = (data != 0);
580 | }
581 |
582 | #ifdef DEBUG_VERBOSE
583 | Serial.print("Addr was 0x");
584 | Serial.print(addr, HEX);
585 | Serial.print(" and data was 0x");
586 | Serial.println(data, HEX);
587 | #endif
588 | break;
589 |
590 | default:
591 | identify();
592 | #ifdef DEBUG
593 | Serial.print(millis(), DEC);
594 | Serial.println(" | Unknown command received!!");
595 | #endif
596 | break;
597 |
598 | }
599 |
600 | interrupts();
601 |
602 | if (rumble != rumblelast)
603 | {
604 | rumblelast = rumble;
605 | // control rumble motor
606 | analogWrite(RUMBLE_PIN, (rumble?RUMBLE_FORCE:0));
607 | }
608 |
609 | #ifdef DEBUG_VERBOSE
610 | Serial.print("It was a 0x");
611 | Serial.print(n64_command, HEX);
612 | Serial.println(" command");
613 | #endif
614 | }
615 |
616 | /**
617 | * Waits for an incomming signal on the N64 pin and reads the command,
618 | * and if necessary, any trailing bytes.
619 | * 0x00 is an identify request
620 | * 0x01 is a status request
621 | * 0x02 is a controller pack read
622 | * 0x03 is a controller pack write
623 | * 0xFF is a controller reset and identify request
624 | *
625 | * for 0x02 and 0x03, additional data is passed in after the command byte,
626 | * which is also read by this function.
627 | *
628 | * All data is raw dumped to the n64_raw_dump array, 1 bit per byte, except
629 | * for the command byte, which is placed all packed into n64_command
630 | */
631 | static void get_n64_command(void)
632 | {
633 | int bitcount;
634 | char *bitbin = n64_raw_dump;
635 | int idle_wait;
636 |
637 | n64_command = 0;
638 |
639 | bitcount = 8;
640 |
641 | // wait to make sure the line is idle before
642 | // we begin listening
643 | for (idle_wait=32; idle_wait>0; --idle_wait) {
644 | if (!N64_QUERY) {
645 | idle_wait = 32;
646 | }
647 | }
648 |
649 | read_loop:
650 | // wait for the line to go low
651 | while (N64_QUERY){}
652 |
653 | // wait approx 2us and poll the line
654 | asm volatile (
655 | "nop\nnop\nnop\nnop\nnop\n"
656 | "nop\nnop\nnop\nnop\nnop\n"
657 | "nop\nnop\nnop\nnop\nnop\n"
658 | "nop\nnop\nnop\nnop\nnop\n"
659 | "nop\nnop\nnop\nnop\nnop\n"
660 | "nop\nnop\nnop\nnop\nnop\n"
661 | );
662 | if (N64_QUERY)
663 | n64_command |= 0x01;
664 |
665 | --bitcount;
666 | if (bitcount == 0)
667 | goto read_more;
668 |
669 | n64_command <<= 1;
670 |
671 | // wait for line to go high again
672 | // I don't want this to execute if the loop is exiting, so
673 | // I couldn't use a traditional for-loop
674 | while (!N64_QUERY) {}
675 | goto read_loop;
676 |
677 | read_more:
678 | switch (n64_command)
679 | {
680 | case (0x03):
681 | // write command
682 | // we expect a 2 byte address and 32 bytes of data
683 | bitcount = 272 + 1; // 34 bytes * 8 bits per byte
684 | //Serial.println("command is 0x03, write");
685 | break;
686 | case (0x02):
687 | // read command 0x02
688 | // we expect a 2 byte address
689 | bitcount = 16 + 1;
690 | //Serial.println("command is 0x02, read");
691 | break;
692 | case (0x00):
693 | case (0x01):
694 | case (0xFF):
695 | case (0x7F):
696 | case (0xFD):
697 | case (0xFE):
698 | default:
699 | // get the last (stop) bit
700 | bitcount = 1;
701 | break;
702 | }
703 |
704 | // make sure the line is high. Hopefully we didn't already
705 | // miss the high-to-low transition
706 | while (!N64_QUERY) {}
707 | read_loop2:
708 | // wait for the line to go low
709 | while (N64_QUERY){}
710 |
711 | // wait approx 2us and poll the line
712 | asm volatile (
713 | "nop\nnop\nnop\nnop\nnop\n"
714 | "nop\nnop\nnop\nnop\nnop\n"
715 | "nop\nnop\nnop\nnop\nnop\n"
716 | "nop\nnop\nnop\nnop\nnop\n"
717 | "nop\nnop\nnop\nnop\nnop\n"
718 | "nop\nnop\nnop\nnop\nnop\n"
719 | );
720 | *bitbin = N64_QUERY;
721 | ++bitbin;
722 | --bitcount;
723 | if (bitcount == 0)
724 | return;
725 |
726 | // wait for line to go high again
727 | while (!N64_QUERY) {}
728 | goto read_loop2;
729 | }
730 |
731 | /**
732 | * This sends the given byte sequence to the n64
733 | * length must be at least 1
734 | * hardcoded for Arduino DIO 8
735 | */
736 | static void n64_send(unsigned char *buffer, char length, bool wide_stop)
737 | {
738 | asm volatile (";Starting N64 Send Routine");
739 | // Send these bytes
740 | char bits;
741 |
742 | // This routine is very carefully timed by examining the assembly output.
743 | // Do not change any statements, it could throw the timings off
744 | //
745 | // We get 16 cycles per microsecond, which should be plenty, but we need to
746 | // be conservative. Most assembly ops take 1 cycle, but a few take 2
747 | //
748 | // I use manually constructed for-loops out of gotos so I have more control
749 | // over the outputted assembly. I can insert nops where it was impossible
750 | // with a for loop
751 |
752 | asm volatile (";Starting outer for loop");
753 | outer_loop:
754 | {
755 | asm volatile (";Starting inner for loop");
756 | bits=8;
757 | inner_loop:
758 | {
759 | // Starting a bit, set the line low
760 | asm volatile (";Setting line to low");
761 | N64_LOW; // 1 op, 2 cycles
762 |
763 | asm volatile (";branching");
764 | if (*buffer >> 7) {
765 | asm volatile (";Bit is a 1");
766 | // 1 bit
767 | // remain low for 1us, then go high for 3us
768 | // nop block 1
769 | asm volatile ("nop\nnop\nnop\nnop\nnop\n");
770 |
771 | asm volatile (";Setting line to high");
772 | N64_HIGH;
773 |
774 | // nop block 2
775 | // we'll wait only 2us to sync up with both conditions
776 | // at the bottom of the if statement
777 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
778 | "nop\nnop\nnop\nnop\nnop\n"
779 | "nop\nnop\nnop\nnop\nnop\n"
780 | "nop\nnop\nnop\nnop\nnop\n"
781 | "nop\nnop\nnop\nnop\nnop\n"
782 | "nop\nnop\nnop\nnop\nnop\n"
783 | );
784 |
785 | } else {
786 | asm volatile (";Bit is a 0");
787 | // 0 bit
788 | // remain low for 3us, then go high for 1us
789 | // nop block 3
790 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
791 | "nop\nnop\nnop\nnop\nnop\n"
792 | "nop\nnop\nnop\nnop\nnop\n"
793 | "nop\nnop\nnop\nnop\nnop\n"
794 | "nop\nnop\nnop\nnop\nnop\n"
795 | "nop\nnop\nnop\nnop\nnop\n"
796 | "nop\nnop\nnop\nnop\nnop\n"
797 | "nop\n");
798 |
799 | asm volatile (";Setting line to high");
800 | N64_HIGH;
801 |
802 | // wait for 1us
803 | asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
804 |
805 | }
806 | // end of the if, the line is high and needs to remain
807 | // high for exactly 16 more cycles, regardless of the previous
808 | // branch path
809 |
810 | asm volatile (";finishing inner loop body");
811 | --bits;
812 | if (bits != 0) {
813 | // nop block 4
814 | // this block is why a for loop was impossible
815 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
816 | "nop\nnop\nnop\nnop\n");
817 | // rotate bits
818 | asm volatile (";rotating out bits");
819 | *buffer <<= 1;
820 |
821 | goto inner_loop;
822 | } // fall out of inner loop
823 | }
824 | asm volatile (";continuing outer loop");
825 | // In this case: the inner loop exits and the outer loop iterates,
826 | // there are /exactly/ 16 cycles taken up by the necessary operations.
827 | // So no nops are needed here (that was lucky!)
828 | --length;
829 | if (length != 0) {
830 | ++buffer;
831 | goto outer_loop;
832 | } // fall out of outer loop
833 | }
834 |
835 | // send a single stop (1) bit
836 | // nop block 5
837 | asm volatile ("nop\nnop\nnop\nnop\n");
838 | N64_LOW;
839 | // wait 1 us, 16 cycles, then raise the line
840 | // take another 3 off for the wide_stop check
841 | // 16-2-3=11
842 | // nop block 6
843 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
844 | "nop\nnop\nnop\nnop\nnop\n"
845 | "nop\n");
846 | if (wide_stop) {
847 | asm volatile (";another 1us for extra wide stop bit\n"
848 | "nop\nnop\nnop\nnop\nnop\n"
849 | "nop\nnop\nnop\nnop\nnop\n"
850 | "nop\nnop\nnop\nnop\n");
851 | }
852 |
853 | N64_HIGH;
854 |
855 | }
856 |
857 | /**
858 | * This sends the given byte sequence to the n64 without a stop bit
859 | * length must be at least 1
860 | * hardcoded for Arduino DIO 8
861 | */
862 | static void n64_send_raw(unsigned char *buffer, char length)
863 | {
864 | asm volatile (";Starting N64 Send Routine");
865 | // Send these bytes
866 | char bits;
867 |
868 | // This routine is very carefully timed by examining the assembly output.
869 | // Do not change any statements, it could throw the timings off
870 | //
871 | // We get 16 cycles per microsecond, which should be plenty, but we need to
872 | // be conservative. Most assembly ops take 1 cycle, but a few take 2
873 | //
874 | // I use manually constructed for-loops out of gotos so I have more control
875 | // over the outputted assembly. I can insert nops where it was impossible
876 | // with a for loop
877 |
878 | asm volatile (";Starting outer for loop");
879 | outer_loop:
880 | {
881 | asm volatile (";Starting inner for loop");
882 | bits=8;
883 | inner_loop:
884 | {
885 | // Starting a bit, set the line low
886 | asm volatile (";Setting line to low");
887 | N64_LOW; // 1 op, 2 cycles
888 |
889 | asm volatile (";branching");
890 | if (*buffer >> 7) {
891 | asm volatile (";Bit is a 1");
892 | // 1 bit
893 | // remain low for 1us, then go high for 3us
894 | // nop block 1
895 | asm volatile ("nop\nnop\nnop\nnop\nnop\n");
896 |
897 | asm volatile (";Setting line to high");
898 | N64_HIGH;
899 |
900 | // nop block 2
901 | // we'll wait only 2us to sync up with both conditions
902 | // at the bottom of the if statement
903 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
904 | "nop\nnop\nnop\nnop\nnop\n"
905 | "nop\nnop\nnop\nnop\nnop\n"
906 | "nop\nnop\nnop\nnop\nnop\n"
907 | "nop\nnop\nnop\nnop\nnop\n"
908 | "nop\nnop\nnop\nnop\nnop\n"
909 | );
910 |
911 | } else {
912 | asm volatile (";Bit is a 0");
913 | // 0 bit
914 | // remain low for 3us, then go high for 1us
915 | // nop block 3
916 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
917 | "nop\nnop\nnop\nnop\nnop\n"
918 | "nop\nnop\nnop\nnop\nnop\n"
919 | "nop\nnop\nnop\nnop\nnop\n"
920 | "nop\nnop\nnop\nnop\nnop\n"
921 | "nop\nnop\nnop\nnop\nnop\n"
922 | "nop\nnop\nnop\nnop\nnop\n"
923 | "nop\n");
924 |
925 | asm volatile (";Setting line to high");
926 | N64_HIGH;
927 |
928 | // wait for 1us
929 | asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
930 |
931 | }
932 | // end of the if, the line is high and needs to remain
933 | // high for exactly 16 more cycles, regardless of the previous
934 | // branch path
935 |
936 | asm volatile (";finishing inner loop body");
937 | --bits;
938 | if (bits != 0) {
939 | // nop block 4
940 | // this block is why a for loop was impossible
941 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
942 | "nop\nnop\nnop\nnop\n");
943 | // rotate bits
944 | asm volatile (";rotating out bits");
945 | *buffer <<= 1;
946 |
947 | goto inner_loop;
948 | } // fall out of inner loop
949 | }
950 | asm volatile (";continuing outer loop");
951 | // In this case: the inner loop exits and the outer loop iterates,
952 | // there are /exactly/ 16 cycles taken up by the necessary operations.
953 | // So no nops are needed here (that was lucky!)
954 | --length;
955 | if (length != 0) {
956 | ++buffer;
957 | goto outer_loop;
958 | } // fall out of outer loop
959 | }
960 | N64_HIGH;
961 | }
962 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # atmega328-N64-controller
2 | A stand alone replacement to the Nintendo CNT-NUS N64 controller chip, with built in rumble, for use in portables.
3 | Tested on 328, 328P, and 168 (all @ 16 Mhz)
4 |
5 | *If memory pack support isnt required just use "N64_controller_replacement.ino"
6 |
7 | *Memory pack emulation is WIP and currently does not work.
8 |
9 | *Rumble is not working with goldeneye
10 |
11 |
12 | Based off: Gamecube controller to Nintendo 64 adapter
13 | by Andrew Brown
14 | https://github.com/brownan/Gamecube-N64-Controller/blob/master/gamecube.ino
15 | Thank you Andrew
16 |
17 |
18 | Rewritten so the atmega328 can be used as a stand alone replacement to the Nintendo CNT-NUS N64 controller chip, with built in rumble, for use in portables.
19 |
20 | To use, hook up the following to the Arduino Atmega328
21 | Digital I/O 8: N64 serial line
22 | Digital I/O 10: All appropriate grounding and power lines, joystick, and all buttons (all active low, see pin definitions in .ino and wiring diagrams)
23 |
24 | Shoutout to pears from bitbuilt.net for testing.
25 |
--------------------------------------------------------------------------------
/crc_table.h:
--------------------------------------------------------------------------------
1 | /**
2 | * This CRC table for repeating bytes is take from
3 | * the cube64 project
4 | * http://cia.vc/stats/project/navi-misc/cube64
5 | */
6 | unsigned char crc_repeating_table[] = {
7 | 0xFF, // 0x00
8 | 0x14, // 0x01
9 | 0xAC, // 0x02
10 | 0x47, // 0x03
11 | 0x59, // 0x04
12 | 0xB2, // 0x05
13 | 0x0A, // 0x06
14 | 0xE1, // 0x07
15 | 0x36, // 0x08
16 | 0xDD, // 0x09
17 | 0x65, // 0x0A
18 | 0x8E, // 0x0B
19 | 0x90, // 0x0C
20 | 0x7B, // 0x0D
21 | 0xC3, // 0x0E
22 | 0x28, // 0x0F
23 | 0xE8, // 0x10
24 | 0x03, // 0x11
25 | 0xBB, // 0x12
26 | 0x50, // 0x13
27 | 0x4E, // 0x14
28 | 0xA5, // 0x15
29 | 0x1D, // 0x16
30 | 0xF6, // 0x17
31 | 0x21, // 0x18
32 | 0xCA, // 0x19
33 | 0x72, // 0x1A
34 | 0x99, // 0x1B
35 | 0x87, // 0x1C
36 | 0x6C, // 0x1D
37 | 0xD4, // 0x1E
38 | 0x3F, // 0x1F
39 | 0xD1, // 0x20
40 | 0x3A, // 0x21
41 | 0x82, // 0x22
42 | 0x69, // 0x23
43 | 0x77, // 0x24
44 | 0x9C, // 0x25
45 | 0x24, // 0x26
46 | 0xCF, // 0x27
47 | 0x18, // 0x28
48 | 0xF3, // 0x29
49 | 0x4B, // 0x2A
50 | 0xA0, // 0x2B
51 | 0xBE, // 0x2C
52 | 0x55, // 0x2D
53 | 0xED, // 0x2E
54 | 0x06, // 0x2F
55 | 0xC6, // 0x30
56 | 0x2D, // 0x31
57 | 0x95, // 0x32
58 | 0x7E, // 0x33
59 | 0x60, // 0x34
60 | 0x8B, // 0x35
61 | 0x33, // 0x36
62 | 0xD8, // 0x37
63 | 0x0F, // 0x38
64 | 0xE4, // 0x39
65 | 0x5C, // 0x3A
66 | 0xB7, // 0x3B
67 | 0xA9, // 0x3C
68 | 0x42, // 0x3D
69 | 0xFA, // 0x3E
70 | 0x11, // 0x3F
71 | 0xA3, // 0x40
72 | 0x48, // 0x41
73 | 0xF0, // 0x42
74 | 0x1B, // 0x43
75 | 0x05, // 0x44
76 | 0xEE, // 0x45
77 | 0x56, // 0x46
78 | 0xBD, // 0x47
79 | 0x6A, // 0x48
80 | 0x81, // 0x49
81 | 0x39, // 0x4A
82 | 0xD2, // 0x4B
83 | 0xCC, // 0x4C
84 | 0x27, // 0x4D
85 | 0x9F, // 0x4E
86 | 0x74, // 0x4F
87 | 0xB4, // 0x50
88 | 0x5F, // 0x51
89 | 0xE7, // 0x52
90 | 0x0C, // 0x53
91 | 0x12, // 0x54
92 | 0xF9, // 0x55
93 | 0x41, // 0x56
94 | 0xAA, // 0x57
95 | 0x7D, // 0x58
96 | 0x96, // 0x59
97 | 0x2E, // 0x5A
98 | 0xC5, // 0x5B
99 | 0xDB, // 0x5C
100 | 0x30, // 0x5D
101 | 0x88, // 0x5E
102 | 0x63, // 0x5F
103 | 0x8D, // 0x60
104 | 0x66, // 0x61
105 | 0xDE, // 0x62
106 | 0x35, // 0x63
107 | 0x2B, // 0x64
108 | 0xC0, // 0x65
109 | 0x78, // 0x66
110 | 0x93, // 0x67
111 | 0x44, // 0x68
112 | 0xAF, // 0x69
113 | 0x17, // 0x6A
114 | 0xFC, // 0x6B
115 | 0xE2, // 0x6C
116 | 0x09, // 0x6D
117 | 0xB1, // 0x6E
118 | 0x5A, // 0x6F
119 | 0x9A, // 0x70
120 | 0x71, // 0x71
121 | 0xC9, // 0x72
122 | 0x22, // 0x73
123 | 0x3C, // 0x74
124 | 0xD7, // 0x75
125 | 0x6F, // 0x76
126 | 0x84, // 0x77
127 | 0x53, // 0x78
128 | 0xB8, // 0x79
129 | 0x00, // 0x7A
130 | 0xEB, // 0x7B
131 | 0xF5, // 0x7C
132 | 0x1E, // 0x7D
133 | 0xA6, // 0x7E
134 | 0x4D, // 0x7F
135 | 0x47, // 0x80
136 | 0xAC, // 0x81
137 | 0x14, // 0x82
138 | 0xFF, // 0x83
139 | 0xE1, // 0x84
140 | 0x0A, // 0x85
141 | 0xB2, // 0x86
142 | 0x59, // 0x87
143 | 0x8E, // 0x88
144 | 0x65, // 0x89
145 | 0xDD, // 0x8A
146 | 0x36, // 0x8B
147 | 0x28, // 0x8C
148 | 0xC3, // 0x8D
149 | 0x7B, // 0x8E
150 | 0x90, // 0x8F
151 | 0x50, // 0x90
152 | 0xBB, // 0x91
153 | 0x03, // 0x92
154 | 0xE8, // 0x93
155 | 0xF6, // 0x94
156 | 0x1D, // 0x95
157 | 0xA5, // 0x96
158 | 0x4E, // 0x97
159 | 0x99, // 0x98
160 | 0x72, // 0x99
161 | 0xCA, // 0x9A
162 | 0x21, // 0x9B
163 | 0x3F, // 0x9C
164 | 0xD4, // 0x9D
165 | 0x6C, // 0x9E
166 | 0x87, // 0x9F
167 | 0x69, // 0xA0
168 | 0x82, // 0xA1
169 | 0x3A, // 0xA2
170 | 0xD1, // 0xA3
171 | 0xCF, // 0xA4
172 | 0x24, // 0xA5
173 | 0x9C, // 0xA6
174 | 0x77, // 0xA7
175 | 0xA0, // 0xA8
176 | 0x4B, // 0xA9
177 | 0xF3, // 0xAA
178 | 0x18, // 0xAB
179 | 0x06, // 0xAC
180 | 0xED, // 0xAD
181 | 0x55, // 0xAE
182 | 0xBE, // 0xAF
183 | 0x7E, // 0xB0
184 | 0x95, // 0xB1
185 | 0x2D, // 0xB2
186 | 0xC6, // 0xB3
187 | 0xD8, // 0xB4
188 | 0x33, // 0xB5
189 | 0x8B, // 0xB6
190 | 0x60, // 0xB7
191 | 0xB7, // 0xB8
192 | 0x5C, // 0xB9
193 | 0xE4, // 0xBA
194 | 0x0F, // 0xBB
195 | 0x11, // 0xBC
196 | 0xFA, // 0xBD
197 | 0x42, // 0xBE
198 | 0xA9, // 0xBF
199 | 0x1B, // 0xC0
200 | 0xF0, // 0xC1
201 | 0x48, // 0xC2
202 | 0xA3, // 0xC3
203 | 0xBD, // 0xC4
204 | 0x56, // 0xC5
205 | 0xEE, // 0xC6
206 | 0x05, // 0xC7
207 | 0xD2, // 0xC8
208 | 0x39, // 0xC9
209 | 0x81, // 0xCA
210 | 0x6A, // 0xCB
211 | 0x74, // 0xCC
212 | 0x9F, // 0xCD
213 | 0x27, // 0xCE
214 | 0xCC, // 0xCF
215 | 0x0C, // 0xD0
216 | 0xE7, // 0xD1
217 | 0x5F, // 0xD2
218 | 0xB4, // 0xD3
219 | 0xAA, // 0xD4
220 | 0x41, // 0xD5
221 | 0xF9, // 0xD6
222 | 0x12, // 0xD7
223 | 0xC5, // 0xD8
224 | 0x2E, // 0xD9
225 | 0x96, // 0xDA
226 | 0x7D, // 0xDB
227 | 0x63, // 0xDC
228 | 0x88, // 0xDD
229 | 0x30, // 0xDE
230 | 0xDB, // 0xDF
231 | 0x35, // 0xE0
232 | 0xDE, // 0xE1
233 | 0x66, // 0xE2
234 | 0x8D, // 0xE3
235 | 0x93, // 0xE4
236 | 0x78, // 0xE5
237 | 0xC0, // 0xE6
238 | 0x2B, // 0xE7
239 | 0xFC, // 0xE8
240 | 0x17, // 0xE9
241 | 0xAF, // 0xEA
242 | 0x44, // 0xEB
243 | 0x5A, // 0xEC
244 | 0xB1, // 0xED
245 | 0x09, // 0xEE
246 | 0xE2, // 0xEF
247 | 0x22, // 0xF0
248 | 0xC9, // 0xF1
249 | 0x71, // 0xF2
250 | 0x9A, // 0xF3
251 | 0x84, // 0xF4
252 | 0x6F, // 0xF5
253 | 0xD7, // 0xF6
254 | 0x3C, // 0xF7
255 | 0xEB, // 0xF8
256 | 0x00, // 0xF9
257 | 0xB8, // 0xFA
258 | 0x53, // 0xFB
259 | 0x4D, // 0xFC
260 | 0xA6, // 0xFD
261 | 0x1E, // 0xFE
262 | 0xF5 // 0xFF
263 | };
264 |
--------------------------------------------------------------------------------
/encoder plug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtryba/atmega328-N64-controller/e7e30b0f5fb9462d51dd0affc4642ec36c89d67f/encoder plug.png
--------------------------------------------------------------------------------
/mini_n64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtryba/atmega328-N64-controller/e7e30b0f5fb9462d51dd0affc4642ec36c89d67f/mini_n64.png
--------------------------------------------------------------------------------
/n64_controller_replacement_memory.ino:
--------------------------------------------------------------------------------
1 | /**
2 | * Gamecube controller to Nintendo 64 adapter
3 | * by Andrew Brown
4 | * https://github.com/brownan/Gamecube-N64-Controller/blob/master/gamecube.ino
5 | *
6 | *
7 | * Rewritten by jtryba so the atmega328 can be used as a stand alone replacement to the Nintendo CNT-NUS N64 controller chip, with built in rumble, for use in portables.
8 | * Idealy for the Arduino Nano or Arduino Pro Mini, As long as it uses an ATmega328, ATmega328P, or ATmega168 @ 16Mhz.
9 | * Optionally supports original N64 encoder joystick or memory pak emulation.
10 | * (not enough pins for both, we could use a single anolog pin for the d-pad but would require more external resistors, see link below)
11 | * https://forum.arduino.cc/index.php?topic=8558.0
12 | *
13 | * Note: Holding L and R while pressing start will recalibrate the joystick, this feature was programmed into real n64 controllers, and i liked the idea so i kept it.
14 | * Note: Holding L and R while pressing Z will switch between rumble mode and memory pack mode.
15 | *
16 | *
17 | * Emulates a CPack using a 24LC256 32k I2C EEPROM
18 | * http://ww1.microchip.com/downloads/en/DeviceDoc/24AA256-24LC256-24FC256-Data-Sheet-20001203W.pdf
19 | * Save manager can be found here:
20 | * https://bryc.github.io/mempak
21 | * The above link is where i got an empty cpack.N64 file from (CPack file)
22 | * after obtaining a fresh cpack.N64 file i converted it to a cpack.MPK using:
23 | * https://beckabney.com/mk64/mempak.php
24 | * then wrote the cpack.MPK raw data to the eeprom before use on a n64.
25 | *
26 | * MPK is standard for EverDrives and most virtual mempaks in emulators.
27 | * DexDrive saves have .N64 extensions.
28 | * Raphnet-tech_adapter_manager dumps also have .N64
29 | *
30 | * raphnet-tech_adapter_manager can be found here:
31 | * https://www.raphnet-tech.com/products/adapter_manager/index.php
32 | * N64 to USB adapter - V3 required to use the above program, that can be found here:
33 | * https://www.raphnet-tech.com/products/n64_usb_adapter_gen3/index.php
34 | *
35 | * N64 controller protocol can be found here:
36 | * https://code.google.com/archive/p/micro-64-controller/wikis/Protocol.wiki
37 | *
38 | *
39 | * To use, hook up the following to the Atmega (I used the 328P-PU and the 168)
40 | * Digital I/O 8: N64 serial line
41 | * Digital I/O 10: 220 ohm resistor to 2n2222 base, emitter to ground, collector to motor using 1N4007 flyback diode accross motor legs, and finally motor to 5v
42 | * All appropriate grounding and power lines, joystick, and all buttons (all active low, see pin definitions below)
43 | *
44 | * /------------\
45 | * / O O O \
46 | * | 3.3V Signl GND |
47 | * |________________|
48 | * (Front of N64)
49 | *
50 | * The pin-out for the N64 and Gamecube wires can be found here:
51 | * http://svn.navi.cx/misc/trunk/wasabi/devices/cube64/hardware/cube64-basic.pdf
52 | * Note: that diagram is not for this project, but for a similar project which
53 | * uses a PIC microcontroller. However, the diagram does describe the pinouts
54 | * of the gamecube and N64 wires.
55 | *
56 | * Also note: the N64 supplies a 3.3 volt line, but I don't plug that into
57 | * anything. The arduino can't run off of that many volts, it needs more, so
58 | * it's powered externally. Therefore, only two lines
59 | * from the N64 are used.
60 | *
61 | * Just use the 5v rail to power this in a portable.
62 | */
63 |
64 | //#define USE_ENCODER 1 // uncomment this line to use the orig n64 encoder, only nano supports this
65 | //#define USE_EEPROM 1 // uncomment this line to use the 24LC256 to emulate a cpack
66 |
67 | #if defined(ARDUINO_AVR_MINI)
68 | #define BOARD "Mini"
69 | #elif defined(ARDUINO_AVR_NANO)
70 | #define BOARD "Nano"
71 | #elif defined(ARDUINO_AVR_PRO)
72 | #define BOARD "Pro"
73 | #elif defined(ARDUINO_AVR_UNO)
74 | #define BOARD "Uno"
75 | #else
76 | #error "Unsupported board"
77 | #endif
78 |
79 | #define F_CPU 16000000
80 |
81 | #define N64_PIN 8
82 | #define RUMBLE_PIN 10
83 | #define RUMBLE_FORCE 250 //0-255 were using a PWM signal to control rumble to save battery
84 | #define N64_HIGH DDRB &= ~0x01
85 | #define N64_LOW DDRB |= 0x01
86 | #define N64_QUERY (PINB & 0x01)
87 |
88 | #include
89 | #include "pins_arduino.h"
90 | #include "crc_table.h"
91 |
92 | #ifdef USE_EEPROM
93 | #if defined(ARDUINO_AVR_MINI)
94 | #error("This is board does not support cpack emulation, not enough pins!")
95 | #endif
96 | /*This address is determined by the way your address pins are wired.
97 | I connected A0 and A1 to Ground and A2 to 5V. To get the address,
98 | we start with the control code from the datasheet (1010) and add
99 | the logic state for each address pin in the order A2, A1, A0 (100)
100 | which gives us 0b1010100, or in Hexadecimal, 0x54*/
101 | #define EEPROM_ADR 0x54
102 |
103 | /*The 24LC256 has a 64-byte page write buffer but N64 writes 32 at a time*/
104 | #define I2C_PAGE 32
105 |
106 | #define I2C_CLOCK 400000
107 |
108 | // eeprom page buffers
109 | byte tempStore[I2C_PAGE];
110 | byte eepromData[I2C_PAGE];
111 | #endif
112 |
113 | #ifdef USE_ENCODER
114 |
115 | #if defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_PRO)
116 | #error("This is board does not support n64 encoder, not enough pins!")
117 | #elif defined(ARDUINO_AVR_NANO)
118 | #define encoderIx A1 // 1
119 | #define encoderQx A2 // 4
120 | #define encoderIy A6 // 5
121 | #define encoderQy A7 // 6 (white)
122 | #elif defined(ARDUINO_AVR_UNO)
123 | #if defined(USE_EEPROM)
124 | #error("Can NOT enable USE_ENCODER and USE_EEPROM simultaneously! The encoder makes use of the two I2C pins that are required by the EEPROM.")
125 | #endif
126 | #define encoderIx A1 // 1
127 | #define encoderQx A2 // 4
128 | #define encoderIy A4 // 5
129 | #define encoderQy A5 // 6 (white)
130 | #else
131 | #error("This is board does not support the encoder! Not enough pins, must use Arduino Nano.")
132 | #endif
133 |
134 | volatile signed int countx;
135 | volatile signed int county;
136 | #else
137 | #define JOY_DEAD 2
138 | #define JOY_RANGE 400 // 1023 * 0.4 rounded a bit
139 | #define JOY_X A1
140 | #define JOY_Y A2
141 | //#define I2C_SCL A4
142 | //#define I2C_SDA A5
143 | #endif
144 |
145 | #define BUTTON_COUNT 14
146 | #define BTN_A 0
147 | #define BTN_B 1
148 | #define BTN_C_UP 2
149 | #define BTN_C_DOWN 3
150 | #define BTN_C_LEFT 4
151 | #define BTN_C_RIGHT 5
152 | #define BTN_L 9
153 | #define BTN_R A0
154 | #define BTN_Z 11
155 | #define PAD_UP 6
156 | #define PAD_DOWN 7
157 | #define PAD_LEFT 12
158 | #define PAD_RIGHT A3
159 | #define BTN_START 13
160 |
161 | // Control sticks:
162 | // N64 expects a signed value from -128 to 128 with 0 being neutral
163 | //
164 | // Additionally, the 64 controllers are relative. Whatever stick position
165 | // it's in when it's powered on is what it reports as 0.
166 | //
167 | // While the joystick data is a signed 8 bit 2s complement we know from Micro
168 | // that controllers only have 160 steps on them and I've had games which screw
169 | // up when given the full 8 bit range.
170 | //
171 | // 160 steps diveded by 2 is 80 steps in each direction however,
172 | // by using the controller_test.rom on official hardware,
173 | // I've found that most controllers report closer to 90-100 anyway
174 | //
175 | // More technical info on joysticks can be found here:
176 | // http://n64devkit.square7.ch/pro-man/pro26/26-02.htm#01
177 | //
178 | #define JOY_MAX_REPORT 100 // 127 for full 8 bit data
179 |
180 | int JOY_X_MIN = 0; // will be calculated later in CalStick()
181 | int JOY_X_MAX = 1023; // will be calculated later in CalStick()
182 | int JOY_Y_MIN = 0; // will be calculated later in CalStick()
183 | int JOY_Y_MAX = 1023; // will be calculated later in CalStick()
184 |
185 | // Zero points for the controller stick
186 | static unsigned char zero_x;
187 | static unsigned char zero_y;
188 |
189 | const int btn[BUTTON_COUNT] =
190 | {
191 | BTN_A, BTN_B, BTN_Z, BTN_START, PAD_UP, PAD_DOWN, PAD_LEFT, PAD_RIGHT,
192 | BTN_L, BTN_R, BTN_C_UP, BTN_C_DOWN, BTN_C_LEFT, BTN_C_RIGHT
193 | };
194 |
195 | static char n64_raw_dump[281]; // maximum recv is 1+2+32 bytes + 1 bit
196 | // n64_raw_dump does /not/ include the command byte. That gets pushed into
197 | // n64_command:
198 | static unsigned char n64_command;
199 |
200 | // bytes to send to the 64
201 | // maximum we'll need to send is 33, 32 for a read request and 1 CRC byte
202 | static unsigned char n64_buffer[33];
203 |
204 | static void get_n64_command(void);
205 | static void n64_send(unsigned char *buffer, char length, bool wide_stop);
206 | void ReadInputs(void);
207 | void CalStick(void);
208 | signed int GetStick_x(void);
209 | signed int GetStick_y(void);
210 | #ifdef USE_EEPROM
211 | static word addrCRC(word address);
212 | //static byte dataCRC(byte * data);
213 | void readEEPROMPage(long eeAddress);
214 | void writeEEPROMPage(long eeAddress);
215 | long lastSwitch = millis();
216 | #endif
217 | #ifdef USE_ENCODER
218 | void handleEncoderX(void);
219 | void handleEncoderX(void);
220 | #endif
221 |
222 | static bool rumble = false;
223 | static bool rumble_mode = true;
224 |
225 | void setup()
226 | {
227 | // Communication with the N64 on this pin
228 | digitalWrite(N64_PIN, LOW);
229 | pinMode(N64_PIN, INPUT);
230 |
231 | /*
232 | Serial.begin(9600);
233 | Serial.println();
234 | Serial.println("Setup has started!");
235 | */
236 |
237 | // setup I/O
238 | // stick
239 | #ifdef USE_ENCODER
240 | countx=0;
241 | county=0;
242 | pinMode(encoderIx, INPUT);
243 | pinMode(encoderQx, INPUT);
244 | pinMode(encoderIy, INPUT);
245 | pinMode(encoderQy, INPUT);
246 | attachInterrupt(digitalPinToInterrupt(encoderIx), handleEncoderX, CHANGE);
247 | attachInterrupt(digitalPinToInterrupt(encoderIy), handleEncoderY, CHANGE);
248 | #else
249 | pinMode(JOY_X, INPUT);
250 | pinMode(JOY_Y, INPUT);
251 | #endif
252 |
253 | // buttons
254 | for (int i = 0; i < BUTTON_COUNT; i ++)
255 | {
256 | digitalWrite(btn[i], LOW);
257 | pinMode(btn[i], INPUT_PULLUP);
258 | }
259 |
260 | // rumble
261 | digitalWrite(RUMBLE_PIN, LOW);
262 | pinMode(RUMBLE_PIN, OUTPUT);
263 |
264 | #ifndef USE_ENCODER
265 | CalStick();
266 | #endif
267 |
268 | #ifdef USE_EEPROM
269 | //Start the I2C Library
270 | Wire.begin();
271 | Wire.setClock(I2C_CLOCK);
272 | #endif
273 |
274 | //Serial.println("Code has started!");
275 | }
276 |
277 | void ReadInputs(void)
278 | {
279 | // clear it out
280 | memset(n64_buffer, 0, sizeof(n64_buffer));
281 |
282 | // buttons
283 | // First byte in n64_buffer should contain:
284 | // A, B, Z, Start, Dup, Ddown, Dleft, Dright
285 | bitWrite(n64_buffer[0], 7, !digitalRead(btn[0]));
286 | bitWrite(n64_buffer[0], 6, !digitalRead(btn[1]));
287 | bitWrite(n64_buffer[0], 5, !digitalRead(btn[2]));
288 | bitWrite(n64_buffer[0], 4, !digitalRead(btn[3]));
289 | bitWrite(n64_buffer[0], 3, !digitalRead(btn[4]));
290 | bitWrite(n64_buffer[0], 2, !digitalRead(btn[5]));
291 | bitWrite(n64_buffer[0], 1, !digitalRead(btn[6]));
292 | bitWrite(n64_buffer[0], 0, !digitalRead(btn[7]));
293 |
294 | // Second byte to N64 should contain:
295 | // Reset, 0, L, R, Cup, Cdown, Cleft, Cright
296 | //bitWrite(n64_buffer[1], 7, 0); // used below
297 | //bitWrite(n64_buffer[1], 6, 0); // unknown bit, padding?
298 | bitWrite(n64_buffer[1], 5, !digitalRead(btn[8]));
299 | bitWrite(n64_buffer[1], 4, !digitalRead(btn[9]));
300 | bitWrite(n64_buffer[1], 3, !digitalRead(btn[10]));
301 | bitWrite(n64_buffer[1], 2, !digitalRead(btn[11]));
302 | bitWrite(n64_buffer[1], 1, !digitalRead(btn[12]));
303 | bitWrite(n64_buffer[1], 0, !digitalRead(btn[13]));
304 |
305 | // user reset the controller
306 | byte l = bitRead(n64_buffer[1], 5);
307 | byte r = bitRead(n64_buffer[1], 4);
308 | byte start = bitRead(n64_buffer[0], 4);
309 | byte z = bitRead(n64_buffer[0], 5);
310 | if (l != 0 && r != 0)
311 | {
312 | if (start != 0)
313 | {
314 | //Serial.println("User reset the controller");
315 | bitWrite(n64_buffer[1], 7, 1); // set controller reset bit
316 | bitWrite(n64_buffer[0], 4, 0); // ignore start press
317 | CalStick();
318 | }
319 | #ifdef USE_EEPROM
320 | else if (z != 0)
321 | {
322 | long elapsed = lastSwitch - millis();
323 | if (elapsed > 500)
324 | {
325 | rumble_mode = !rumble_mode;
326 | lastSwitch = millis();
327 | }
328 | }
329 | #endif
330 | }
331 |
332 | // Third byte: Control Stick X position
333 | n64_buffer[2] = -zero_x + GetStick_x();
334 | // Fourth byte: Control Stick Y Position
335 | n64_buffer[3] = -zero_y + GetStick_y();
336 |
337 | // next 2 lines ignore joystick data, for testing
338 | //n64_buffer[2] = 0;
339 | //n64_buffer[3] = 0;
340 |
341 | /*
342 | char buf[32];
343 | memset(buf, 0, 32);
344 | sprintf(buf, "0x%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
345 | bitRead(n64_buffer[0], 7),
346 | bitRead(n64_buffer[0], 6),
347 | bitRead(n64_buffer[0], 5),
348 | bitRead(n64_buffer[0], 4),
349 | bitRead(n64_buffer[0], 3),
350 | bitRead(n64_buffer[0], 2),
351 | bitRead(n64_buffer[0], 1),
352 | bitRead(n64_buffer[0], 0),
353 | bitRead(n64_buffer[1], 5),
354 | bitRead(n64_buffer[1], 4),
355 | bitRead(n64_buffer[1], 3),
356 | bitRead(n64_buffer[1], 2),
357 | bitRead(n64_buffer[1], 1),
358 | bitRead(n64_buffer[1], 0)
359 | );
360 | Serial.print(buf);
361 | Serial.print(" ");
362 | Serial.print(n64_buffer[2], HEX);
363 | Serial.print(" ");
364 | Serial.print(n64_buffer[3], HEX);
365 | Serial.print(" ");
366 | Serial.println(rumble, HEX);
367 | */
368 | }
369 |
370 | void CalStick(void)
371 | {
372 | //Serial.println("Calibrating analog stick...");
373 | #ifdef USE_ENCODER
374 | zero_x = 0;
375 | zero_y = 0;
376 | #else
377 | int t = 5;
378 | int x = 0;
379 | int y = 0;
380 | for (int i = 0; i < t; i++)
381 | {
382 | x += analogRead(JOY_X);
383 | y += analogRead(JOY_Y);
384 | }
385 |
386 | int center_x = x/t;
387 | int center_y = y/t;
388 |
389 | JOY_X_MIN = constrain(center_x-JOY_RANGE, 0, 1023);
390 | JOY_X_MAX = constrain(center_x+JOY_RANGE, 0, 1023);
391 | JOY_Y_MIN = constrain(center_y-JOY_RANGE, 0, 1023);
392 | JOY_Y_MAX = constrain(center_y+JOY_RANGE, 0, 1023);
393 |
394 | zero_x = GetStick_x();
395 | zero_y = GetStick_y();
396 | #endif
397 |
398 | /*
399 | Serial.print("Center x: ");
400 | Serial.println(center_x);
401 | Serial.print("Center y: ");
402 | Serial.println(center_y);
403 | Serial.println("Calibration complete!");
404 | */
405 | }
406 |
407 | #ifdef USE_ENCODER
408 |
409 | void handleEncoderX()
410 | {
411 | if(digitalRead(encoderIx) == digitalRead(encoderQx))
412 | {
413 | countx++;
414 | }
415 | else
416 | {
417 | countx--;
418 | }
419 | countx = constrain(countx, -JOY_MAX_REPORT, JOY_MAX_REPORT);
420 | }
421 |
422 | void handleEncoderY()
423 | {
424 | if(digitalRead(encoderIy) == digitalRead(encoderQy))
425 | {
426 | county++;
427 | }
428 | else
429 | {
430 | county--;
431 | }
432 | county = constrain(county, -JOY_MAX_REPORT, JOY_MAX_REPORT);
433 | }
434 |
435 | #endif
436 |
437 | signed int GetStick_x(void)
438 | {
439 | #ifdef USE_ENCODER
440 | return countx;
441 | #else
442 | unsigned int l = analogRead(JOY_X);
443 | l = constrain(l, JOY_X_MIN, JOY_X_MAX);
444 | signed int i = map(l, JOY_X_MIN, JOY_X_MAX, -JOY_MAX_REPORT, JOY_MAX_REPORT);
445 | if (i < JOY_DEAD && i > -JOY_DEAD)
446 | return 0;
447 | return i;
448 | #endif
449 | }
450 |
451 | signed int GetStick_y(void)
452 | {
453 | #ifdef USE_ENCODER
454 | return county;
455 | #else
456 | unsigned int l = analogRead(JOY_Y);
457 | l = constrain(l, JOY_Y_MIN, JOY_Y_MAX);
458 | signed int i = map(l, JOY_Y_MIN, JOY_Y_MAX, -JOY_MAX_REPORT, JOY_MAX_REPORT);
459 | if (i < JOY_DEAD && i > -JOY_DEAD)
460 | return 0;
461 | return i;
462 | #endif
463 | }
464 |
465 | void loop()
466 | {
467 | int status;
468 | unsigned char data, addr;
469 |
470 | // control rumble motor
471 | analogWrite(RUMBLE_PIN, (rumble?RUMBLE_FORCE:0));
472 |
473 | ReadInputs();
474 |
475 | // Wait for incomming 64 command
476 | // this will block until the N64 sends us a command
477 | noInterrupts();
478 | get_n64_command();
479 |
480 | // 0x00 is identify command
481 | // 0x01 is status
482 | // 0x02 is read
483 | // 0x03 is write
484 | // 0xFF is reset and identify
485 | //
486 | // More info on reading and writing can be found here:
487 | // http://ultra64.ca/files/documentation/online-manuals/man/n64man/misc/glossarySystem.html#:~:text=Each%20256%20bytes%20is%20called,system%20for%20game%20note%20management.
488 | // and here:
489 | // http://n64devkit.square7.ch/pro-man/pro26/26-03.htm
490 | // and here:
491 | // http://hcs64.com/files/n64-hw.dox
492 | // and here:
493 | // https://github.com/sanni/cartreader/blob/b7dd3866700ca299e015ce86dfabd027bb4d17fc/Cart_Reader/N64.ino#L1367
494 | //
495 |
496 | switch (n64_command)
497 | {
498 | case 0xFF:
499 | CalStick();
500 | rumble = false;
501 | case 0x00:
502 | // identify
503 | // mutilate the n64_buffer array with our status
504 | // we return 0x050001 to indicate we have a rumble pack
505 | // or 0x050002 to indicate the expansion slot is empty
506 | //
507 | // 0xFF I've seen sent from Mario 64 and Shadows of the Empire.
508 | // I don't know why it's different, but the controllers seem to
509 | // send a set of status bytes afterwards the same as 0x00, and
510 | // it won't work without it.
511 | n64_buffer[0] = 0x05;
512 | n64_buffer[1] = 0x00;
513 | n64_buffer[2] = 0x01;
514 |
515 | n64_send(n64_buffer, 3, 0);
516 |
517 | //Serial.println("It was 0x00: an identify command");
518 | break;
519 | case 0x01:
520 | // blast out the pre-assembled array in n64_buffer
521 | n64_send(n64_buffer, 4, 0);
522 |
523 | //Serial.println("It was 0x01: the query command");
524 | break;
525 | case 0x02:
526 | // was the address the rumble latch at 0x8000?
527 | // decode the first half of the address, bits
528 | // 8 through 15
529 | addr = 0;
530 | addr |= (n64_raw_dump[0] != 0) << 7;
531 | addr |= (n64_raw_dump[1] != 0) << 6;
532 | addr |= (n64_raw_dump[2] != 0) << 5;
533 | addr |= (n64_raw_dump[3] != 0) << 4;
534 | addr |= (n64_raw_dump[4] != 0) << 3;
535 | addr |= (n64_raw_dump[5] != 0) << 2;
536 | addr |= (n64_raw_dump[6] != 0) << 1;
537 | addr |= (n64_raw_dump[7] != 0);
538 |
539 | // A read. If the address is 0x8000, return 32 bytes of 0x80 bytes,
540 | // and a CRC byte. this tells the system our attached controller
541 | // pack is a rumble pack
542 |
543 | unsigned int myAddress = 0x0000;
544 |
545 | // Assume it's a read for 0x8000, which is the only thing it should
546 | // be requesting anyways (in rumble mode)
547 | if (addr == 0x8000) {
548 | memset(n64_buffer, rumble_mode?0x80:0x00, 32);
549 | n64_buffer[32] = 0xB8; // CRC
550 | n64_send(n64_buffer, 33, 1);
551 | }
552 | #ifdef USE_EEPROM
553 | else if (!rumble_mode)
554 | {
555 | myAddress |= (n64_raw_dump[0] != 0) << 15;
556 | myAddress |= (n64_raw_dump[1] != 0) << 14;
557 | myAddress |= (n64_raw_dump[2] != 0) << 13;
558 | myAddress |= (n64_raw_dump[3] != 0) << 12;
559 | myAddress |= (n64_raw_dump[4] != 0) << 11;
560 | myAddress |= (n64_raw_dump[5] != 0) << 10;
561 | myAddress |= (n64_raw_dump[6] != 0) << 9;
562 | myAddress |= (n64_raw_dump[7] != 0) << 8;
563 | myAddress |= (n64_raw_dump[8] != 0) << 7;
564 | myAddress |= (n64_raw_dump[9] != 0) << 6;
565 | myAddress |= (n64_raw_dump[10] != 0) << 5;
566 | myAddress |= (n64_raw_dump[11] != 0) << 4;
567 | myAddress |= (n64_raw_dump[12] != 0) << 3;
568 | myAddress |= (n64_raw_dump[13] != 0) << 2;
569 | myAddress |= (n64_raw_dump[14] != 0) << 1;
570 | myAddress |= (n64_raw_dump[15] != 0);
571 |
572 | readEEPROMPage(myAddress);
573 | memset(n64_buffer, 0x00, 32);
574 | for (long i = 0; i < 32; i++)
575 | {
576 | n64_buffer[i] = eepromData[i];
577 | }
578 |
579 | // TODO test this, not sure if the CRC is being calculated correctly here
580 | n64_buffer[32] = addrCRC(myAddress);
581 | n64_send(n64_buffer, 33, 1);
582 | }
583 | #endif
584 |
585 | /*
586 | Serial.println("It was 0x02: the read command");
587 | Serial.print("Addr was 0x");
588 | Serial.print(myAddress, HEX);
589 | Serial.println(" and data was:");
590 | for (int i = 0; i< 32; i++)
591 | {
592 | Serial.print("Addr:0x");
593 | Serial.print(myAddress+i, HEX);
594 | Serial.print(" data:0x");
595 | Serial.print(n64_buffer[i], HEX);
596 | }
597 | Serial.print(" and crc was 0x");
598 | Serial.print(n64_buffer[32], HEX);
599 | */
600 | break;
601 | case 0x03:
602 | // A write. we at least need to respond with a single CRC byte. If
603 | // the write was to address 0xC000 and the data was 0x01, turn on
604 | // rumble! All other write addresses are ignored. (but we still
605 | // need to return a CRC)
606 |
607 | // decode the first data byte (fourth overall byte), bits indexed
608 | // at 24 through 31
609 | data = 0;
610 | data |= (n64_raw_dump[16] != 0) << 7;
611 | data |= (n64_raw_dump[17] != 0) << 6;
612 | data |= (n64_raw_dump[18] != 0) << 5;
613 | data |= (n64_raw_dump[19] != 0) << 4;
614 | data |= (n64_raw_dump[20] != 0) << 3;
615 | data |= (n64_raw_dump[21] != 0) << 2;
616 | data |= (n64_raw_dump[22] != 0) << 1;
617 | data |= (n64_raw_dump[23] != 0);
618 |
619 | // get crc byte, invert it, as per the protocol for
620 | // having a memory card attached
621 | n64_buffer[0] = crc_repeating_table[data] ^ 0xFF;
622 |
623 | // send it
624 | n64_send(n64_buffer, 1, 1);
625 |
626 | // end of time critical code
627 |
628 | // was the address the rumble latch at 0xC000?
629 | // decode the first half of the address, bits
630 | // 8 through 15
631 | addr = 0;
632 | addr |= (n64_raw_dump[0] != 0) << 7;
633 | addr |= (n64_raw_dump[1] != 0) << 6;
634 | addr |= (n64_raw_dump[2] != 0) << 5;
635 | addr |= (n64_raw_dump[3] != 0) << 4;
636 | addr |= (n64_raw_dump[4] != 0) << 3;
637 | addr |= (n64_raw_dump[5] != 0) << 2;
638 | addr |= (n64_raw_dump[6] != 0) << 1;
639 | addr |= (n64_raw_dump[7] != 0);
640 |
641 | if (addr == 0xC0 && rumble_mode) {
642 | //Rumble pak writes:
643 | //To switch on the rumble pak motor, the N64 sends:
644 | //03 C0 1B 01 01 01 ...
645 | //This writes 01 to addresses starting at 0x4000.
646 |
647 | //To turn the motor back off, the N64 sends:
648 | //03 C0 1B 00 00 00 ...
649 | rumble = (data != 0);
650 | }
651 | #ifdef USE_EEPROM
652 | else
653 | {
654 | unsigned int myAddress = 0x0000;
655 | myAddress |= (n64_raw_dump[0] != 0) << 15;
656 | myAddress |= (n64_raw_dump[1] != 0) << 14;
657 | myAddress |= (n64_raw_dump[2] != 0) << 13;
658 | myAddress |= (n64_raw_dump[3] != 0) << 12;
659 | myAddress |= (n64_raw_dump[4] != 0) << 11;
660 | myAddress |= (n64_raw_dump[5] != 0) << 10;
661 | myAddress |= (n64_raw_dump[6] != 0) << 9;
662 | myAddress |= (n64_raw_dump[7] != 0) << 8;
663 | myAddress |= (n64_raw_dump[8] != 0) << 7;
664 | myAddress |= (n64_raw_dump[9] != 0) << 6;
665 | myAddress |= (n64_raw_dump[10] != 0) << 5;
666 | myAddress |= (n64_raw_dump[11] != 0) << 4;
667 | myAddress |= (n64_raw_dump[12] != 0) << 3;
668 | myAddress |= (n64_raw_dump[13] != 0) << 2;
669 | myAddress |= (n64_raw_dump[14] != 0) << 1;
670 | myAddress |= (n64_raw_dump[15] != 0);
671 |
672 | long currentSpot = 0;
673 | long timerReset = 0;
674 | byte counter = 0;
675 |
676 | long len = sizeof(n64_raw_dump);
677 | memset(tempStore, 0x00, I2C_PAGE); // clear tempStore
678 |
679 | for (int i = 16; i < len-8; i++) // first 16 bits are address, last 8 are crc
680 | {
681 | tempStore[counter++] = n64_raw_dump[i];
682 |
683 | if (counter == I2C_PAGE)
684 | {
685 | //Once we've collected a page worth, go ahead and do
686 | //a page write operation
687 | writeEEPROMPage(myAddress+currentSpot);
688 | counter = 0; //Reset count
689 | currentSpot += I2C_PAGE;
690 | }
691 | }
692 | }
693 | #endif
694 | /*
695 | Serial.println("It was 0x03: the write command");
696 | Serial.print("Addr was 0x");
697 | Serial.print(myAddress, HEX);
698 | Serial.println(" and data was:");
699 | for (int i = 0; i< 32; i++)
700 | {
701 | Serial.print("Addr:0x");
702 | Serial.print(myAddress+i, HEX);
703 | Serial.print(" data:0x");
704 | Serial.print(tempStore[i], HEX);
705 | }
706 | */
707 | break;
708 |
709 | case 0x04:
710 | //Serial.println("It was 0x04: the CRC error command received!!");
711 | break;
712 |
713 | default:
714 | //Serial.print(millis(), DEC);
715 | //Serial.println(" | Unknown command received!!");
716 | break;
717 |
718 | }
719 |
720 | interrupts();
721 | }
722 |
723 | #ifdef USE_EEPROM
724 | void writeEEPROMPage(long eeAddress)
725 | {
726 | //Serial.print("Writing EEPROM page to address: 0x");
727 | //Serial.print(eeAddress, HEX);
728 | //Serial.print(" to 0x");
729 | //Serial.println(eeAddress+I2C_PAGE, HEX);
730 | Wire.beginTransmission(EEPROM_ADR);
731 |
732 | Wire.write((int)(eeAddress >> 8)); // MSB
733 | Wire.write((int)(eeAddress & 0xFF)); // LSB
734 |
735 | //Write bytes to EEPROM
736 | for (byte x = 0 ; x < I2C_PAGE ; x++)
737 | Wire.write(tempStore[x]); //Write the data
738 |
739 | Wire.endTransmission(); //Send stop condition
740 |
741 | memset(tempStore, 0x00, I2C_PAGE); // clear tempStore
742 | }
743 |
744 | void readEEPROMPage(long eeAddress)
745 | {
746 | //Serial.print("Reading EEPROM page from address: 0x");
747 | //Serial.print(eeAddress, HEX);
748 | //Serial.print(" to 0x");
749 | //Serial.println(eeAddress+I2C_PAGE, HEX);
750 | memset(eepromData, 0x00, I2C_PAGE); // clear page
751 | unsigned char i=0;
752 | Wire.beginTransmission(EEPROM_ADR);
753 | Wire.write((int)(eeAddress >> 8)); // MSB
754 | Wire.write((int)(eeAddress & 0xFF)); // LSB
755 | Wire.endTransmission();
756 |
757 | Wire.requestFrom(EEPROM_ADR,I2C_PAGE);
758 |
759 | while(Wire.available())
760 | eepromData[i++] = Wire.read();
761 | }
762 | #endif
763 |
764 | /**
765 | * Waits for an incomming signal on the N64 pin and reads the command,
766 | * and if necessary, any trailing bytes.
767 | * 0x00 is an identify request
768 | * 0x01 is a status request
769 | * 0x02 is a controller pack read
770 | * 0x03 is a controller pack write
771 | * 0xFF is a controller reset and identify request
772 | *
773 | * for 0x02 and 0x03, additional data is passed in after the command byte,
774 | * which is also read by this function.
775 | *
776 | * All data is raw dumped to the n64_raw_dump array, 1 bit per byte, except
777 | * for the command byte, which is placed all packed into n64_command
778 | */
779 | static void get_n64_command(void)
780 | {
781 | int bitcount;
782 | char *bitbin = n64_raw_dump;
783 | int idle_wait;
784 |
785 | n64_command = 0;
786 |
787 | bitcount = 8;
788 |
789 | // wait to make sure the line is idle before
790 | // we begin listening
791 | for (idle_wait=32; idle_wait>0; --idle_wait) {
792 | if (!N64_QUERY) {
793 | idle_wait = 32;
794 | }
795 | }
796 |
797 | read_loop:
798 | // wait for the line to go low
799 | while (N64_QUERY){}
800 |
801 | // wait approx 2us and poll the line
802 | asm volatile (
803 | "nop\nnop\nnop\nnop\nnop\n"
804 | "nop\nnop\nnop\nnop\nnop\n"
805 | "nop\nnop\nnop\nnop\nnop\n"
806 | "nop\nnop\nnop\nnop\nnop\n"
807 | "nop\nnop\nnop\nnop\nnop\n"
808 | "nop\nnop\nnop\nnop\nnop\n"
809 | );
810 | if (N64_QUERY)
811 | n64_command |= 0x01;
812 |
813 | --bitcount;
814 | if (bitcount == 0)
815 | goto read_more;
816 |
817 | n64_command <<= 1;
818 |
819 | // wait for line to go high again
820 | // I don't want this to execute if the loop is exiting, so
821 | // I couldn't use a traditional for-loop
822 | while (!N64_QUERY) {}
823 | goto read_loop;
824 |
825 | read_more:
826 | switch (n64_command)
827 | {
828 | case (0x03):
829 | // write command
830 | // we expect a 2 byte address and 32 bytes of data
831 | bitcount = 272 + 1; // 34 bytes * 8 bits per byte
832 | //Serial.println("command is 0x03, write");
833 | break;
834 | case (0x02):
835 | // read command 0x02
836 | // we expect a 2 byte address
837 | bitcount = 16 + 1;
838 | //Serial.println("command is 0x02, read");
839 | break;
840 | case (0x00):
841 | case (0x01):
842 | case (0xFF):
843 | default:
844 | // get the last (stop) bit
845 | bitcount = 1;
846 | break;
847 | }
848 |
849 | // make sure the line is high. Hopefully we didn't already
850 | // miss the high-to-low transition
851 | while (!N64_QUERY) {}
852 | read_loop2:
853 | // wait for the line to go low
854 | while (N64_QUERY){}
855 |
856 | // wait approx 2us and poll the line
857 | asm volatile (
858 | "nop\nnop\nnop\nnop\nnop\n"
859 | "nop\nnop\nnop\nnop\nnop\n"
860 | "nop\nnop\nnop\nnop\nnop\n"
861 | "nop\nnop\nnop\nnop\nnop\n"
862 | "nop\nnop\nnop\nnop\nnop\n"
863 | "nop\nnop\nnop\nnop\nnop\n"
864 | );
865 | *bitbin = N64_QUERY;
866 | ++bitbin;
867 | --bitcount;
868 | if (bitcount == 0)
869 | return;
870 |
871 | // wait for line to go high again
872 | while (!N64_QUERY) {}
873 | goto read_loop2;
874 | }
875 |
876 | /**
877 | * This sends the given byte sequence to the n64
878 | * length must be at least 1
879 | * hardcoded for Arduino DIO 8
880 | */
881 | static void n64_send(unsigned char *buffer, char length, bool wide_stop)
882 | {
883 | asm volatile (";Starting N64 Send Routine");
884 | // Send these bytes
885 | char bits;
886 |
887 | // This routine is very carefully timed by examining the assembly output.
888 | // Do not change any statements, it could throw the timings off
889 | //
890 | // We get 16 cycles per microsecond, which should be plenty, but we need to
891 | // be conservative. Most assembly ops take 1 cycle, but a few take 2
892 | //
893 | // I use manually constructed for-loops out of gotos so I have more control
894 | // over the outputted assembly. I can insert nops where it was impossible
895 | // with a for loop
896 |
897 | asm volatile (";Starting outer for loop");
898 | outer_loop:
899 | {
900 | asm volatile (";Starting inner for loop");
901 | bits=8;
902 | inner_loop:
903 | {
904 | // Starting a bit, set the line low
905 | asm volatile (";Setting line to low");
906 | N64_LOW; // 1 op, 2 cycles
907 |
908 | asm volatile (";branching");
909 | if (*buffer >> 7) {
910 | asm volatile (";Bit is a 1");
911 | // 1 bit
912 | // remain low for 1us, then go high for 3us
913 | // nop block 1
914 | asm volatile ("nop\nnop\nnop\nnop\nnop\n");
915 |
916 | asm volatile (";Setting line to high");
917 | N64_HIGH;
918 |
919 | // nop block 2
920 | // we'll wait only 2us to sync up with both conditions
921 | // at the bottom of the if statement
922 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
923 | "nop\nnop\nnop\nnop\nnop\n"
924 | "nop\nnop\nnop\nnop\nnop\n"
925 | "nop\nnop\nnop\nnop\nnop\n"
926 | "nop\nnop\nnop\nnop\nnop\n"
927 | "nop\nnop\nnop\nnop\nnop\n"
928 | );
929 |
930 | } else {
931 | asm volatile (";Bit is a 0");
932 | // 0 bit
933 | // remain low for 3us, then go high for 1us
934 | // nop block 3
935 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
936 | "nop\nnop\nnop\nnop\nnop\n"
937 | "nop\nnop\nnop\nnop\nnop\n"
938 | "nop\nnop\nnop\nnop\nnop\n"
939 | "nop\nnop\nnop\nnop\nnop\n"
940 | "nop\nnop\nnop\nnop\nnop\n"
941 | "nop\nnop\nnop\nnop\nnop\n"
942 | "nop\n");
943 |
944 | asm volatile (";Setting line to high");
945 | N64_HIGH;
946 |
947 | // wait for 1us
948 | asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
949 |
950 | }
951 | // end of the if, the line is high and needs to remain
952 | // high for exactly 16 more cycles, regardless of the previous
953 | // branch path
954 |
955 | asm volatile (";finishing inner loop body");
956 | --bits;
957 | if (bits != 0) {
958 | // nop block 4
959 | // this block is why a for loop was impossible
960 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
961 | "nop\nnop\nnop\nnop\n");
962 | // rotate bits
963 | asm volatile (";rotating out bits");
964 | *buffer <<= 1;
965 |
966 | goto inner_loop;
967 | } // fall out of inner loop
968 | }
969 | asm volatile (";continuing outer loop");
970 | // In this case: the inner loop exits and the outer loop iterates,
971 | // there are /exactly/ 16 cycles taken up by the necessary operations.
972 | // So no nops are needed here (that was lucky!)
973 | --length;
974 | if (length != 0) {
975 | ++buffer;
976 | goto outer_loop;
977 | } // fall out of outer loop
978 | }
979 |
980 | // send a single stop (1) bit
981 | // nop block 5
982 | asm volatile ("nop\nnop\nnop\nnop\n");
983 | N64_LOW;
984 | // wait 1 us, 16 cycles, then raise the line
985 | // take another 3 off for the wide_stop check
986 | // 16-2-3=11
987 | // nop block 6
988 | asm volatile ("nop\nnop\nnop\nnop\nnop\n"
989 | "nop\nnop\nnop\nnop\nnop\n"
990 | "nop\n");
991 | if (wide_stop) {
992 | asm volatile (";another 1us for extra wide stop bit\n"
993 | "nop\nnop\nnop\nnop\nnop\n"
994 | "nop\nnop\nnop\nnop\nnop\n"
995 | "nop\nnop\nnop\nnop\n");
996 | }
997 |
998 | N64_HIGH;
999 |
1000 | }
1001 |
1002 | #ifdef USE_EEPROM
1003 | /******************************************
1004 | N64 Controller CRC Functions
1005 | *****************************************/
1006 | static word addrCRC(word address) {
1007 | // CRC table
1008 | word xor_table[16] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x15, 0x1F, 0x0B, 0x16, 0x19, 0x07, 0x0E, 0x1C, 0x0D, 0x1A, 0x01 };
1009 | word crc = 0;
1010 | // Make sure we have a valid address
1011 | address &= ~0x1F;
1012 | // Go through each bit in the address, and if set, xor the right value into the output
1013 | for (int i = 15; i >= 5; i--) {
1014 | // Is this bit set?
1015 | if ( ((address >> i) & 0x1)) {
1016 | crc ^= xor_table[i];
1017 | }
1018 | }
1019 | // Just in case
1020 | crc &= 0x1F;
1021 | // Create a new address with the CRC appended
1022 | return address | crc;
1023 | }
1024 |
1025 | /* unused
1026 | static byte dataCRC(byte * data) {
1027 | byte ret = 0;
1028 | for (byte i = 0; i <= 32; i++) {
1029 | for (byte j = 7; j >= 0; j--) {
1030 | int tmp = 0;
1031 | if (ret & 0x80) {
1032 | tmp = 0x85;
1033 | }
1034 | ret <<= 1;
1035 | if ( i < 32 ) {
1036 | if (data[i] & (0x01 << j)) {
1037 | ret |= 0x1;
1038 | }
1039 | }
1040 | ret ^= tmp;
1041 | }
1042 | }
1043 | return ret;
1044 | }
1045 | */
1046 | #endif
1047 |
--------------------------------------------------------------------------------
/n64_power.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtryba/atmega328-N64-controller/e7e30b0f5fb9462d51dd0affc4642ec36c89d67f/n64_power.png
--------------------------------------------------------------------------------
/nano_n64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtryba/atmega328-N64-controller/e7e30b0f5fb9462d51dd0affc4642ec36c89d67f/nano_n64.png
--------------------------------------------------------------------------------
/rumble_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtryba/atmega328-N64-controller/e7e30b0f5fb9462d51dd0affc4642ec36c89d67f/rumble_all.png
--------------------------------------------------------------------------------
/uno_n64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtryba/atmega328-N64-controller/e7e30b0f5fb9462d51dd0affc4642ec36c89d67f/uno_n64.png
--------------------------------------------------------------------------------
/uno_n64r.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jtryba/atmega328-N64-controller/e7e30b0f5fb9462d51dd0affc4642ec36c89d67f/uno_n64r.png
--------------------------------------------------------------------------------