Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Gamecube controller to Nintendo 64 adapter by Andrew Brown
- // Rewritten for N64 to HID by Peter Den Hartog
- // Rewritten for ControllerTest_16x2 by sanni
- #include <LiquidCrystal.h>
- // Pins for LCD
- LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
- // Pin for controller data
- #define N64_PIN 2
- // DDR register on port D(Arduino pins 0-7)
- #define N64_PIN_DIR DDRD
- // these two macros set arduino pin 2 to input or output, which with an
- // external 1K pull-up resistor to the 3.3V rail, is like pulling it high or
- // low. These operations translate to 1 op code, which takes 2 cycles
- // 0x04 = 00000100 -> Pin 2 = Output
- #define N64_HIGH DDRD &= ~0x04
- #define N64_LOW DDRD |= 0x04
- #define N64_QUERY (PIND & 0x04)
- // received Controller data
- char N64_raw_dump[33]; // 1 received bit per byte
- String rawStr = ""; // above char array read into a string
- struct {
- char stick_x;
- char stick_y;
- }
- N64_status;
- // enable debug mode
- int debug = 0;
- // String with button name
- String button = "";
- void N64_send(unsigned char *buffer, char length);
- void N64_get();
- void setup()
- {
- // Communication with controller on this pin
- // Don't remove these lines, we don't want to push +5V to the controller
- digitalWrite(N64_PIN, LOW);
- pinMode(N64_PIN, INPUT);
- // set up the LCD's number of columns and rows:
- lcd.begin(16, 2);
- }
- // This sends the given byte sequence to the controller
- // length must be at least 1
- // Oh, it destroys the buffer passed in as it writes it
- void N64_send(unsigned char *buffer, char length)
- {
- // Send these bytes
- char bits;
- bool bit;
- // This routine is very carefully timed by examining the assembly output.
- // Do not change any statements, it could throw the timings off
- //
- // We get 16 cycles per microsecond, which should be plenty, but we need to
- // be conservative. Most assembly ops take 1 cycle, but a few take 2
- //
- // I use manually constructed for-loops out of gotos so I have more control
- // over the outputted assembly. I can insert nops where it was impossible
- // with a for loop
- asm volatile (";Starting outer for loop");
- outer_loop:
- {
- asm volatile (";Starting inner for loop");
- bits=8;
- inner_loop:
- {
- // Starting a bit, set the line low
- asm volatile (";Setting line to low");
- N64_LOW; // 1 op, 2 cycles
- asm volatile (";branching");
- if (*buffer >> 7) {
- asm volatile (";Bit is a 1");
- // 1 bit
- // remain low for 1us, then go high for 3us
- // nop block 1
- asm volatile ("nop\nnop\nnop\nnop\nnop\n");
- asm volatile (";Setting line to high");
- N64_HIGH;
- // nop block 2
- // we'll wait only 2us to sync up with both conditions
- // at the bottom of the if statement
- asm volatile ("nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- );
- }
- else {
- asm volatile (";Bit is a 0");
- // 0 bit
- // remain low for 3us, then go high for 1us
- // nop block 3
- asm volatile ("nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\n");
- asm volatile (";Setting line to high");
- N64_HIGH;
- // wait for 1us
- asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
- }
- // end of the if, the line is high and needs to remain
- // high for exactly 16 more cycles, regardless of the previous
- // branch path
- asm volatile (";finishing inner loop body");
- --bits;
- if (bits != 0) {
- // nop block 4
- // this block is why a for loop was impossible
- asm volatile ("nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\n");
- // rotate bits
- asm volatile (";rotating out bits");
- *buffer <<= 1;
- goto inner_loop;
- } // fall out of inner loop
- }
- asm volatile (";continuing outer loop");
- // In this case: the inner loop exits and the outer loop iterates,
- // there are /exactly/ 16 cycles taken up by the necessary operations.
- // So no nops are needed here (that was lucky!)
- --length;
- if (length != 0) {
- ++buffer;
- goto outer_loop;
- } // fall out of outer loop
- }
- // send a single stop (1) bit
- // nop block 5
- asm volatile ("nop\nnop\nnop\nnop\n");
- N64_LOW;
- // wait 1 us, 16 cycles, then raise the line
- // 16-2=14
- // nop block 6
- asm volatile ("nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\n");
- N64_HIGH;
- }
- void N64_get()
- {
- // listen for the expected 8 bytes of data back from the controller and
- // blast it out to the N64_raw_dump array, one bit per byte for extra speed.
- // Afterwards, call translate_raw_data() to interpret the raw data and pack
- // it into the N64_status struct.
- asm volatile (";Starting to listen");
- unsigned char timeout;
- char bitcount = 32;
- char *bitbin = N64_raw_dump;
- // Again, using gotos here to make the assembly more predictable and
- // optimization easier (please don't kill me)
- read_loop:
- timeout = 0x3f;
- // wait for line to go low
- while (N64_QUERY) {
- if (!--timeout)
- return;
- }
- // wait approx 2us and poll the line
- asm volatile (
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- "nop\nnop\nnop\nnop\nnop\n"
- );
- *bitbin = N64_QUERY;
- ++bitbin;
- --bitcount;
- if (bitcount == 0)
- return;
- // wait for line to go high again
- // it may already be high, so this should just drop through
- timeout = 0x3f;
- while (!N64_QUERY) {
- if (!--timeout)
- return;
- }
- goto read_loop;
- }
- void get_button()
- {
- // Command to send to the gamecube
- // The last bit is rumble, flip it to rumble
- // yes this does need to be inside the loop, the
- // array gets mutilated when it goes through N64_send
- unsigned char command[] = {
- 0x01 };
- // don't want interrupts getting in the way
- noInterrupts();
- // send those 3 bytes
- N64_send(command, 1);
- // read in data and dump it to N64_raw_dump
- N64_get();
- // end of time sensitive code
- interrupts();
- // The get_N64_status function sloppily dumps its data 1 bit per byte
- // into the get_status_extended char array. It's our job to go through
- // that and put each piece neatly into the struct N64_status
- int i;
- memset(&N64_status, 0, sizeof(N64_status));
- // bits: joystick x value
- // These are 8 bit values centered at 0x80 (128)
- for (i=0; i<8; i++) {
- N64_status.stick_x |= N64_raw_dump[16+i] ? (0x80 >> i) : 0;
- }
- for (i=0; i<8; i++) {
- N64_status.stick_y |= N64_raw_dump[24+i] ? (0x80 >> i) : 0;
- }
- // read char array N64_raw_dump into string rawStr
- rawStr = "";
- for (i=0; i<16; i++) {
- rawStr = rawStr + String(N64_raw_dump[i],DEC);
- }
- // Buttons (A,B,Z,S,DU,DD,DL,DR,0,0,L,R,CU,CD,CL,CR)
- if (rawStr.substring(0,16) == "0000000000000000") {
- button = "Press a Button ";
- }
- else if (rawStr.substring(0,16) == "0040000000440000") {
- button = "In Game Reset ";
- }
- else
- {
- for (int i=0; i<16; i++)
- {
- if(N64_raw_dump[i]==4)
- {
- switch(i)
- {
- case 7:
- button = "DPAD Right";
- break;
- case 6:
- button = "DPAD Left";
- break;
- case 5:
- button = "DPAD Down";
- break;
- case 4:
- button = "DPAD Up";
- break;
- case 3:
- button = "START";
- break;
- case 2:
- button = "Z";
- break;
- case 1:
- button = "B";
- break;
- case 0:
- button = "A";
- break;
- case 15:
- button = "C-Right";
- break;
- case 14:
- button = "C-Left";
- break;
- case 13:
- button = "C-Down";
- break;
- case 12:
- button = "C-Up";
- break;
- case 11:
- button = "R";
- break;
- case 10:
- button = "L";
- break;
- }
- }
- }
- }
- }
- void loop()
- {
- // Get Button and analog stick
- get_button();
- // Print Button
- lcd.setCursor(0, 0);
- if(debug !=1 )
- lcd.print(button + " ");
- else
- lcd.print(rawStr);
- // Print Stick X Value
- String stickx = String("X: " + String(N64_status.stick_x, DEC) + " ");
- lcd.setCursor(0, 1);
- lcd.print(stickx);
- // Print Stick Y Value
- String sticky = String("Y: " + String(N64_status.stick_y, DEC) + " ");
- lcd.setCursor(7, 1);
- lcd.print(sticky);
- } // loop
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement