Advertisement
Guest User

Untitled

a guest
Feb 6th, 2013
208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.15 KB | None | 0 0
  1. // Gamecube controller to Nintendo 64 adapter by Andrew Brown
  2. // Rewritten for N64 to HID by Peter Den Hartog
  3. // Rewritten for ControllerTest_16x2 by sanni
  4.  
  5. #include <LiquidCrystal.h>
  6.  
  7. // Pins for LCD
  8. LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
  9.  
  10. // Pin for controller data
  11. #define N64_PIN 2
  12.  
  13. // DDR register on port D(Arduino pins 0-7)
  14. #define N64_PIN_DIR DDRD
  15.  
  16. // these two macros set arduino pin 2 to input or output, which with an
  17. // external 1K pull-up resistor to the 3.3V rail, is like pulling it high or
  18. // low.  These operations translate to 1 op code, which takes 2 cycles
  19. // 0x04 = 00000100 -> Pin 2 = Output
  20. #define N64_HIGH DDRD &= ~0x04
  21. #define N64_LOW DDRD |= 0x04
  22. #define N64_QUERY (PIND & 0x04)
  23.  
  24. // received Controller data
  25. char N64_raw_dump[33]; // 1 received bit per byte
  26. String rawStr = ""; // above char array read into a string
  27. struct {
  28.   char stick_x;
  29.   char stick_y;
  30. }
  31. N64_status;
  32.  
  33. // enable debug mode
  34. int debug = 0;
  35.  
  36. // String with button name
  37. String button = "";
  38.  
  39. void N64_send(unsigned char *buffer, char length);
  40. void N64_get();
  41.  
  42. void setup()
  43. {
  44.   // Communication with controller on this pin
  45.   // Don't remove these lines, we don't want to push +5V to the controller
  46.   digitalWrite(N64_PIN, LOW);  
  47.   pinMode(N64_PIN, INPUT);
  48.  
  49.   // set up the LCD's number of columns and rows:
  50.   lcd.begin(16, 2);
  51. }
  52.  
  53. // This sends the given byte sequence to the controller
  54. // length must be at least 1
  55. // Oh, it destroys the buffer passed in as it writes it
  56.  
  57. void N64_send(unsigned char *buffer, char length)
  58. {
  59.   // Send these bytes
  60.   char bits;
  61.  
  62.   bool bit;
  63.  
  64.   // This routine is very carefully timed by examining the assembly output.
  65.   // Do not change any statements, it could throw the timings off
  66.   //
  67.   // We get 16 cycles per microsecond, which should be plenty, but we need to
  68.   // be conservative. Most assembly ops take 1 cycle, but a few take 2
  69.   //
  70.   // I use manually constructed for-loops out of gotos so I have more control
  71.   // over the outputted assembly. I can insert nops where it was impossible
  72.   // with a for loop
  73.  
  74.   asm volatile (";Starting outer for loop");
  75. outer_loop:
  76.   {
  77.     asm volatile (";Starting inner for loop");
  78.     bits=8;
  79. inner_loop:
  80.     {
  81.       // Starting a bit, set the line low
  82.       asm volatile (";Setting line to low");
  83.       N64_LOW; // 1 op, 2 cycles
  84.  
  85.       asm volatile (";branching");
  86.       if (*buffer >> 7) {
  87.         asm volatile (";Bit is a 1");
  88.         // 1 bit
  89.         // remain low for 1us, then go high for 3us
  90.         // nop block 1
  91.         asm volatile ("nop\nnop\nnop\nnop\nnop\n");
  92.  
  93.         asm volatile (";Setting line to high");
  94.         N64_HIGH;
  95.  
  96.         // nop block 2
  97.         // we'll wait only 2us to sync up with both conditions
  98.         // at the bottom of the if statement
  99.         asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
  100.           "nop\nnop\nnop\nnop\nnop\n"  
  101.           "nop\nnop\nnop\nnop\nnop\n"  
  102.           "nop\nnop\nnop\nnop\nnop\n"  
  103.           "nop\nnop\nnop\nnop\nnop\n"  
  104.           "nop\nnop\nnop\nnop\nnop\n"  
  105.           );
  106.  
  107.       }
  108.       else {
  109.         asm volatile (";Bit is a 0");
  110.         // 0 bit
  111.         // remain low for 3us, then go high for 1us
  112.         // nop block 3
  113.         asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
  114.           "nop\nnop\nnop\nnop\nnop\n"  
  115.           "nop\nnop\nnop\nnop\nnop\n"  
  116.           "nop\nnop\nnop\nnop\nnop\n"  
  117.           "nop\nnop\nnop\nnop\nnop\n"  
  118.           "nop\nnop\nnop\nnop\nnop\n"  
  119.           "nop\nnop\nnop\nnop\nnop\n"  
  120.           "nop\n");
  121.  
  122.         asm volatile (";Setting line to high");
  123.         N64_HIGH;
  124.  
  125.         // wait for 1us
  126.         asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
  127.  
  128.       }
  129.       // end of the if, the line is high and needs to remain
  130.       // high for exactly 16 more cycles, regardless of the previous
  131.       // branch path
  132.  
  133.       asm volatile (";finishing inner loop body");
  134.       --bits;
  135.       if (bits != 0) {
  136.         // nop block 4
  137.         // this block is why a for loop was impossible
  138.         asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
  139.           "nop\nnop\nnop\nnop\n");
  140.         // rotate bits
  141.         asm volatile (";rotating out bits");
  142.         *buffer <<= 1;
  143.  
  144.         goto inner_loop;
  145.       } // fall out of inner loop
  146.     }
  147.     asm volatile (";continuing outer loop");
  148.     // In this case: the inner loop exits and the outer loop iterates,
  149.     // there are /exactly/ 16 cycles taken up by the necessary operations.
  150.     // So no nops are needed here (that was lucky!)
  151.     --length;
  152.     if (length != 0) {
  153.       ++buffer;
  154.       goto outer_loop;
  155.     } // fall out of outer loop
  156.   }
  157.  
  158.   // send a single stop (1) bit
  159.   // nop block 5
  160.   asm volatile ("nop\nnop\nnop\nnop\n");
  161.   N64_LOW;
  162.   // wait 1 us, 16 cycles, then raise the line
  163.   // 16-2=14
  164.   // nop block 6
  165.   asm volatile ("nop\nnop\nnop\nnop\nnop\n"
  166.     "nop\nnop\nnop\nnop\nnop\n"  
  167.     "nop\nnop\nnop\nnop\n");
  168.   N64_HIGH;
  169.  
  170. }
  171.  
  172. void N64_get()
  173. {
  174.   // listen for the expected 8 bytes of data back from the controller and
  175.   // blast it out to the N64_raw_dump array, one bit per byte for extra speed.
  176.   // Afterwards, call translate_raw_data() to interpret the raw data and pack
  177.   // it into the N64_status struct.
  178.   asm volatile (";Starting to listen");
  179.   unsigned char timeout;
  180.   char bitcount = 32;
  181.   char *bitbin = N64_raw_dump;
  182.  
  183.   // Again, using gotos here to make the assembly more predictable and
  184.   // optimization easier (please don't kill me)
  185. read_loop:
  186.   timeout = 0x3f;
  187.   // wait for line to go low
  188.   while (N64_QUERY) {
  189.     if (!--timeout)
  190.       return;
  191.   }
  192.   // wait approx 2us and poll the line
  193.   asm volatile (
  194.   "nop\nnop\nnop\nnop\nnop\n"  
  195.     "nop\nnop\nnop\nnop\nnop\n"  
  196.     "nop\nnop\nnop\nnop\nnop\n"  
  197.     "nop\nnop\nnop\nnop\nnop\n"  
  198.     "nop\nnop\nnop\nnop\nnop\n"  
  199.     "nop\nnop\nnop\nnop\nnop\n"  
  200.     );
  201.   *bitbin = N64_QUERY;
  202.   ++bitbin;
  203.   --bitcount;
  204.   if (bitcount == 0)
  205.     return;
  206.  
  207.   // wait for line to go high again
  208.   // it may already be high, so this should just drop through
  209.   timeout = 0x3f;
  210.   while (!N64_QUERY) {
  211.     if (!--timeout)
  212.       return;
  213.   }
  214.   goto read_loop;
  215.  
  216. }
  217.  
  218. void get_button()
  219. {
  220.   // Command to send to the gamecube
  221.   // The last bit is rumble, flip it to rumble
  222.   // yes this does need to be inside the loop, the
  223.   // array gets mutilated when it goes through N64_send
  224.   unsigned char command[] = {
  225.     0x01                  };
  226.  
  227.   // don't want interrupts getting in the way
  228.   noInterrupts();
  229.   // send those 3 bytes
  230.   N64_send(command, 1);
  231.   // read in data and dump it to N64_raw_dump
  232.   N64_get();
  233.   // end of time sensitive code
  234.   interrupts();
  235.  
  236.   // The get_N64_status function sloppily dumps its data 1 bit per byte
  237.   // into the get_status_extended char array. It's our job to go through
  238.   // that and put each piece neatly into the struct N64_status
  239.   int i;
  240.   memset(&N64_status, 0, sizeof(N64_status));
  241.  
  242.   // bits: joystick x value
  243.   // These are 8 bit values centered at 0x80 (128)
  244.   for (i=0; i<8; i++) {
  245.     N64_status.stick_x |= N64_raw_dump[16+i] ? (0x80 >> i) : 0;
  246.   }
  247.   for (i=0; i<8; i++) {
  248.     N64_status.stick_y |= N64_raw_dump[24+i] ? (0x80 >> i) : 0;
  249.   }
  250.  
  251.   // read char array N64_raw_dump into string rawStr
  252.   rawStr = "";
  253.   for (i=0; i<16; i++) {
  254.     rawStr = rawStr + String(N64_raw_dump[i],DEC);
  255.   }
  256.  
  257.   // Buttons (A,B,Z,S,DU,DD,DL,DR,0,0,L,R,CU,CD,CL,CR)
  258.   if (rawStr.substring(0,16) == "0000000000000000") {
  259.     button = "Press a Button  ";
  260.   }
  261.   else if (rawStr.substring(0,16) == "0040000000440000") {
  262.     button = "In Game Reset   ";
  263.   }
  264.   else
  265.   {
  266.     for (int i=0; i<16; i++)
  267.     {
  268.       if(N64_raw_dump[i]==4)
  269.       {
  270.         switch(i)
  271.         {
  272.         case 7:
  273.           button = "DPAD Right";
  274.           break;
  275.  
  276.         case 6:
  277.           button = "DPAD Left";
  278.           break;
  279.  
  280.         case 5:
  281.           button = "DPAD Down";
  282.           break;
  283.  
  284.         case 4:
  285.           button = "DPAD Up";
  286.           break;
  287.  
  288.         case 3:
  289.           button = "START";
  290.           break;
  291.  
  292.         case 2:
  293.           button = "Z";
  294.           break;
  295.  
  296.         case 1:
  297.           button = "B";
  298.           break;
  299.  
  300.         case 0:
  301.           button = "A";
  302.           break;
  303.  
  304.         case 15:
  305.           button = "C-Right";
  306.           break;
  307.  
  308.         case 14:
  309.           button = "C-Left";
  310.           break;
  311.  
  312.         case 13:
  313.           button = "C-Down";
  314.           break;
  315.  
  316.         case 12:
  317.           button = "C-Up";
  318.           break;
  319.  
  320.         case 11:
  321.           button = "R";
  322.           break;
  323.  
  324.         case 10:
  325.           button = "L";
  326.           break;
  327.         }
  328.       }
  329.     }
  330.   }
  331. }
  332.  
  333. void loop()
  334. {
  335.   // Get Button and analog stick
  336.   get_button();
  337.  
  338.   // Print Button
  339.   lcd.setCursor(0, 0);  
  340.   if(debug !=1 )
  341.     lcd.print(button + "               ");  
  342.   else
  343.     lcd.print(rawStr);  
  344.  
  345.   // Print Stick X Value
  346.   String stickx = String("X: " + String(N64_status.stick_x, DEC) + "   ");
  347.   lcd.setCursor(0, 1);
  348.   lcd.print(stickx);
  349.  
  350.   // Print Stick Y Value
  351.   String sticky = String("Y: " + String(N64_status.stick_y, DEC) + "   ");
  352.   lcd.setCursor(7, 1);
  353.   lcd.print(sticky);
  354.  
  355. } // loop
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement