├── 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 --------------------------------------------------------------------------------