G2A Many GEOs
SHARE
TWEET

Blw

dragonbane Mar 7th, 2019 26 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include "crc_table.h"
  2.  
  3. #define CTRL_PIN 3
  4. #define CTRL_HIGH DDRD &= ~0x01
  5. #define CTRL_LOW DDRD |= 0x01
  6. #define CTRL_QUERY (PIND & 0x01)
  7.  
  8. #define CNSL_PIN 5
  9. #define CNSL_HIGH DDRC &= ~0x40
  10. #define CNSL_LOW DDRC |= 0x40
  11. #define CNSL_QUERY (PINC & 0x40)
  12.  
  13. //From console to controller
  14. char console_raw_dump[281];
  15. unsigned char console_command;
  16.  
  17. //From controller to console
  18. unsigned char controller_buffer[33];
  19.  
  20. struct state {
  21.   char stick_x;
  22.   char stick_y;
  23.   // bits: 0, 0, 0, start, y, x, b, a
  24.   unsigned char data1;
  25.   // bits: 1, L, R, Z, Dup, Ddown, Dright, Dleft
  26.   unsigned char data2;
  27. } N64_status;
  28.  
  29. void setup()
  30. {
  31.   Serial.begin(9600);
  32.  
  33.   // Communication with the N64-console on this pin
  34.   digitalWrite(CNSL_PIN, LOW);
  35.   pinMode(CNSL_PIN, INPUT);
  36.  
  37.   // Communication with the N64-controller on this pin
  38.   digitalWrite(CTRL_PIN, LOW);
  39.   pinMode(CTRL_PIN, INPUT);  
  40. }
  41.  
  42. void loop()
  43. {
  44.   unsigned char data, addr;
  45.   memset(controller_buffer, 0, sizeof(controller_buffer));
  46.  
  47.    noInterrupts();
  48.   CTRL_get();
  49.    interrupts();
  50.   translate_raw_data();
  51.   print_CTRL_status();
  52.  
  53.   noInterrupts();
  54.  
  55.   get_console_command();
  56.   Serial.println(console_command, HEX);
  57.   switch (console_command)
  58.     {
  59.         case 0x00:
  60.         case 0xFF:
  61.             // identify
  62.             // mutilate the n64_buffer array with our status
  63.             // we return 0x050001 to indicate we have a rumble pack
  64.             // or 0x050002 to indicate the expansion slot is empty
  65.             //
  66.             // 0xFF I've seen sent from Mario 64 and Shadows of the Empire.
  67.             // I don't know why it's different, but the controllers seem to
  68.             // send a set of status bytes afterwards the same as 0x00, and
  69.             // it won't work without it.
  70.             controller_buffer[0] = 0x05;
  71.             controller_buffer[1] = 0x00;
  72.             controller_buffer[2] = 0x02;
  73.  
  74.             console_send(controller_buffer, 3, 0);
  75.  
  76.             //Serial.println("It was 0x00: an identify command");
  77.             break;
  78.         case 0x01:
  79.             // Send to n64 the received data
  80.            
  81.             console_send(controller_buffer, 4, 0);
  82.             //Serial.println("It was 0x01: the query command");
  83.             break;
  84.         case 0x02:
  85.             // A read. If the address is 0x8000, return 32 bytes of 0x80 bytes,
  86.             // and a CRC byte.  this tells the system our attached controller
  87.             // pack is a rumble pack
  88.  
  89.             // Assume it's a read for 0x8000, which is the only thing it should
  90.             // be requesting anyways
  91.             memset(controller_buffer, 0x80, 32);
  92.             controller_buffer[32] = 0xB8; // CRC
  93.  
  94.             console_send(controller_buffer, 33, 1);
  95.  
  96.             //Serial.println("It was 0x02: the read command");
  97.             break;
  98.         case 0x03:
  99.             // A write. we at least need to respond with a single CRC byte.  If
  100.             // the write was to address 0xC000 and the data was 0x01, turn on
  101.             // rumble! All other write addresses are ignored. (but we still
  102.             // need to return a CRC)
  103.  
  104.             // decode the first data byte (fourth overall byte), bits indexed
  105.             // at 24 through 31
  106.             data = 0;
  107.             data |= (console_raw_dump[16] != 0) << 7;
  108.             data |= (console_raw_dump[17] != 0) << 6;
  109.             data |= (console_raw_dump[18] != 0) << 5;
  110.             data |= (console_raw_dump[19] != 0) << 4;
  111.             data |= (console_raw_dump[20] != 0) << 3;
  112.             data |= (console_raw_dump[21] != 0) << 2;
  113.             data |= (console_raw_dump[22] != 0) << 1;
  114.             data |= (console_raw_dump[23] != 0);
  115.  
  116.             // get crc byte, invert it, as per the protocol for
  117.             // having a memory card attached
  118.             controller_buffer[0] = crc_repeating_table[data] ^ 0xFF;
  119.  
  120.             // send it
  121.             console_send(controller_buffer, 1, 1);
  122.  
  123.             // end of time critical code
  124.             // was the address the rumble latch at 0xC000?
  125.             // decode the first half of the address, bits
  126.             // 8 through 15
  127.             addr = 0;
  128.             addr |= (console_raw_dump[0] != 0) << 7;
  129.             addr |= (console_raw_dump[1] != 0) << 6;
  130.             addr |= (console_raw_dump[2] != 0) << 5;
  131.             addr |= (console_raw_dump[3] != 0) << 4;
  132.             addr |= (console_raw_dump[4] != 0) << 3;
  133.             addr |= (console_raw_dump[5] != 0) << 2;
  134.             addr |= (console_raw_dump[6] != 0) << 1;
  135.             addr |= (console_raw_dump[7] != 0);
  136.  
  137.             if (addr == 0xC0) {
  138.             //    rumble = (data != 0);
  139.             }
  140.  
  141.            // Serial.println("It was 0x03: the write command");
  142.             //Serial.print("Addr was 0x");
  143.             //Serial.print(addr, HEX);
  144.             //Serial.print(" and data was 0x");
  145.             //Serial.println(data, HEX);
  146.             break;
  147.  
  148.     }
  149.  
  150. interrupts();
  151. }
  152.  
  153. void translate_raw_data()
  154. {
  155.   memset(&N64_status, 0, sizeof(N64_status));
  156.   for (int i = 0; i < 8; i++) {
  157.     N64_status.data1 |= controller_buffer[i] ? (0x80 >> i) : 0;
  158.     N64_status.data2 |= controller_buffer[8 + i] ? (0x80 >> i) : 0;
  159.     N64_status.stick_x |= controller_buffer[16 + i] ? (0x80 >> i) : 0;
  160.     N64_status.stick_y |= controller_buffer[24 + i] ? (0x80 >> i) : 0;
  161.   }
  162. }
  163.  
  164. void print_CTRL_status()
  165.   {
  166.     char out[30];
  167.     sprintf(out, "%i%i%i%i%i%i%i%i%i%i%i%i%i%i %i %i",
  168.       N64_status.data1&16?1:0,
  169.       N64_status.data1&32?1:0,
  170.       N64_status.data1&64?1:0,
  171.       N64_status.data1&128?1:0,
  172.       N64_status.data2&32?1:0,
  173.       N64_status.data2&16?1:0,
  174.       N64_status.data2&0x08?1:0,
  175.       N64_status.data2&0x04?1:0,
  176.       N64_status.data2&0x01?1:0,
  177.       N64_status.data2&0x02?1:0,
  178.       N64_status.data1&0x08?1:0,
  179.       N64_status.data1&0x04?1:0,
  180.       N64_status.data1&0x01?1:0,
  181.       N64_status.data1&0x02?1:0,
  182.       N64_status.stick_x,
  183.       N64_status.stick_y);
  184.     Serial.println(out);
  185.   }
  186.  
  187. //Send to console
  188. // completly copied and pasted to not mess up timings
  189. void console_send(unsigned char *buffer, char length, bool wide_stop)
  190. {
  191.     asm volatile (";Starting N64 Send Routine");
  192.     // Send these bytes
  193.     char bits;
  194.    
  195.     bool bit;
  196.  
  197.     // This routine is very carefully timed by examining the assembly output.
  198.     // Do not change any statements, it could throw the timings off
  199.     //
  200.     // We get 16 cycles per microsecond, which should be plenty, but we need to
  201.     // be conservative. Most assembly ops take 1 cycle, but a few take 2
  202.     //
  203.     // I use manually constructed for-loops out of gotos so I have more control
  204.     // over the outputted assembly. I can insert nops where it was impossible
  205.     // with a for loop
  206.    
  207.     asm volatile (";Starting outer for loop");
  208. outer_loop:
  209.     {
  210.         asm volatile (";Starting inner for loop");
  211.         bits=8;
  212. inner_loop:
  213.         {
  214.             // Starting a bit, set the line low
  215.             asm volatile (";Setting line to low");
  216.             CNSL_LOW; // 1 op, 2 cycles
  217.  
  218.             asm volatile (";branching");
  219.             if (*buffer >> 7) {
  220.                 asm volatile (";Bit is a 1");
  221.                 // 1 bit
  222.                 // remain low for 1us, then go high for 3us
  223.                 // nop block 1
  224.                 asm volatile ("nop\nnop\nnop\nnop\nnop\n");
  225.                
  226.                 asm volatile (";Setting line to high");
  227.                 CNSL_HIGH;
  228.  
  229.                 // nop block 2
  230.                 // we'll wait only 2us to sync up with both conditions
  231.                 // at the bottom of the if statement
  232.                 asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
  233.                               "nop\nnop\nnop\nnop\nnop\n"  
  234.                               "nop\nnop\nnop\nnop\nnop\n"  
  235.                               "nop\nnop\nnop\nnop\nnop\n"  
  236.                               "nop\nnop\nnop\nnop\nnop\n"  
  237.                               "nop\nnop\nnop\nnop\nnop\n"  
  238.                               );
  239.  
  240.             } else {
  241.                 asm volatile (";Bit is a 0");
  242.                 // 0 bit
  243.                 // remain low for 3us, then go high for 1us
  244.                 // nop block 3
  245.                 asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
  246.                               "nop\nnop\nnop\nnop\nnop\n"  
  247.                               "nop\nnop\nnop\nnop\nnop\n"  
  248.                               "nop\nnop\nnop\nnop\nnop\n"  
  249.                               "nop\nnop\nnop\nnop\nnop\n"  
  250.                               "nop\nnop\nnop\nnop\nnop\n"  
  251.                               "nop\nnop\nnop\nnop\nnop\n"  
  252.                               "nop\n");
  253.  
  254.                 asm volatile (";Setting line to high");
  255.                 CNSL_HIGH;
  256.  
  257.                 // wait for 1us
  258.                 asm volatile ("; end of conditional branch, need to wait 1us more before next bit");
  259.                
  260.             }
  261.             // end of the if, the line is high and needs to remain
  262.             // high for exactly 16 more cycles, regardless of the previous
  263.             // branch path
  264.  
  265.             asm volatile (";finishing inner loop body");
  266.             --bits;
  267.             if (bits != 0) {
  268.                 // nop block 4
  269.                 // this block is why a for loop was impossible
  270.                 asm volatile ("nop\nnop\nnop\nnop\nnop\n"  
  271.                               "nop\nnop\nnop\nnop\n");
  272.                 // rotate bits
  273.                 asm volatile (";rotating out bits");
  274.                 *buffer <<= 1;
  275.  
  276.                 goto inner_loop;
  277.             } // fall out of inner loop
  278.         }
  279.         asm volatile (";continuing outer loop");
  280.         // In this case: the inner loop exits and the outer loop iterates,
  281.         // there are /exactly/ 16 cycles taken up by the necessary operations.
  282.         // So no nops are needed here (that was lucky!)
  283.         --length;
  284.         if (length != 0) {
  285.             ++buffer;
  286.             goto outer_loop;
  287.         } // fall out of outer loop
  288.     }
  289.  
  290.     // send a single stop (1) bit
  291.     // nop block 5
  292.     asm volatile ("nop\nnop\nnop\nnop\n");
  293.     CNSL_LOW;
  294.     // wait 1 us, 16 cycles, then raise the line
  295.     // take another 3 off for the wide_stop check
  296.     // 16-2-3=11
  297.     // nop block 6
  298.     asm volatile ("nop\nnop\nnop\nnop\nnop\n"
  299.                   "nop\nnop\nnop\nnop\nnop\n"  
  300.                   "nop\n");
  301.     if (wide_stop) {
  302.         asm volatile (";another 1us for extra wide stop bit\n"
  303.                       "nop\nnop\nnop\nnop\nnop\n"
  304.                       "nop\nnop\nnop\nnop\nnop\n"  
  305.                       "nop\nnop\nnop\nnop\n");
  306.     }
  307.  
  308.     CNSL_HIGH;
  309. }
  310.  
  311. void get_console_command()
  312. {
  313.  
  314.     int bitcount;
  315.     char *bitbin = console_raw_dump;
  316.     int idle_wait;
  317.  
  318. func_top:
  319.     console_command = 0;
  320.  
  321.     bitcount = 8;
  322.  
  323.     // wait to make sure the line is idle before
  324.     // we begin listening
  325.     for (idle_wait=32; idle_wait>0; --idle_wait) {
  326.         if (!CNSL_QUERY) {
  327.             idle_wait = 32;
  328.         }
  329.     }
  330.  
  331. read_loop:
  332.         // wait for the line to go low
  333.         while (CNSL_QUERY){}
  334.  
  335.         // wait approx 2us and poll the line
  336.         asm volatile (
  337.                       "nop\nnop\nnop\nnop\nnop\n"  
  338.                       "nop\nnop\nnop\nnop\nnop\n"  
  339.                       "nop\nnop\nnop\nnop\nnop\n"  
  340.                       "nop\nnop\nnop\nnop\nnop\n"  
  341.                       "nop\nnop\nnop\nnop\nnop\n"  
  342.                       "nop\nnop\nnop\nnop\nnop\n"  
  343.                 );
  344.         if (CNSL_QUERY)
  345.             console_command |= 0x01;
  346.  
  347.         --bitcount;
  348.         if (bitcount == 0)
  349.             goto read_more;
  350.  
  351.         console_command <<= 1;
  352.  
  353.         // wait for line to go high again
  354.         // I don't want this to execute if the loop is exiting, so
  355.         // I couldn't use a traditional for-loop
  356.         while (!CNSL_QUERY) {}
  357.         goto read_loop;
  358.  
  359. read_more:
  360.         switch (console_command)
  361.         {
  362.             case (0x03):
  363.                 // write command
  364.                 // we expect a 2 byte address and 32 bytes of data
  365.                 bitcount = 272 + 1; // 34 bytes * 8 bits per byte
  366.                 //Serial.println("command is 0x03, write");
  367.                 break;
  368.             case (0x02):
  369.                 // read command 0x02
  370.                 // we expect a 2 byte address
  371.                 bitcount = 16 + 1;
  372.                 //Serial.println("command is 0x02, read");
  373.                 break;
  374.             case (0x00):
  375.             case (0x01):
  376.             default:
  377.                 // get the last (stop) bit
  378.                 bitcount = 1;
  379.                 break;
  380.             //default:
  381.             //    Serial.println(consele_command, HEX);
  382.             //    goto func_top;
  383.         }
  384.  
  385.         // make sure the line is high. Hopefully we didn't already
  386.         // miss the high-to-low transition
  387.         while (!CNSL_QUERY) {}
  388. read_loop2:
  389.         // wait for the line to go low
  390.         while (CNSL_QUERY){}
  391.  
  392.         // wait approx 2us and poll the line
  393.         asm volatile (
  394.                       "nop\nnop\nnop\nnop\nnop\n"  
  395.                       "nop\nnop\nnop\nnop\nnop\n"  
  396.                       "nop\nnop\nnop\nnop\nnop\n"  
  397.                       "nop\nnop\nnop\nnop\nnop\n"  
  398.                       "nop\nnop\nnop\nnop\nnop\n"  
  399.                       "nop\nnop\nnop\nnop\nnop\n"  
  400.                 );
  401.         *bitbin = CNSL_QUERY;
  402.         ++bitbin;
  403.         --bitcount;
  404.         if (bitcount == 0)
  405.             return;
  406.  
  407.         // wait for line to go high again
  408.         while (!CNSL_QUERY) {}
  409.         goto read_loop2;
  410. }
  411.  
  412. void CTRL_get()
  413. {
  414.   // listen for the expected 8 bytes of data back from the controller and
  415.   // blast it out to the N64_raw_dump array, one bit per byte for extra speed.
  416.   // Afterwards, call translate_raw_data() to interpret the raw data and pack
  417.   // it into the N64_status struct.
  418.   asm volatile (";Starting to listen");
  419.   unsigned char timeout;
  420.   char bitcount = 32;
  421.   char *bitbin = controller_buffer;
  422.  
  423.   // Again, using gotos here to make the assembly more predictable and
  424.   // optimization easier (please don't kill me)
  425. read_loop:
  426.   timeout = 0x3f;
  427.   // wait for line to go low
  428.   while (CTRL_QUERY) {
  429.     if (!--timeout)
  430.       return;
  431.   }
  432.   // wait approx 2us and poll the line
  433.   asm volatile (
  434.     "nop\nnop\nnop\nnop\nnop\n"
  435.     "nop\nnop\nnop\nnop\nnop\n"
  436.     "nop\nnop\nnop\nnop\nnop\n"
  437.     "nop\nnop\nnop\nnop\nnop\n"
  438.     "nop\nnop\nnop\nnop\nnop\n"
  439.     "nop\nnop\nnop\nnop\nnop\n"
  440.   );
  441.   *bitbin = CTRL_QUERY;
  442.   ++bitbin;
  443.   --bitcount;
  444.   if (bitcount == 0)
  445.     return;
  446.  
  447.   // wait for line to go high again
  448.   // it may already be high, so this should just drop through
  449.   timeout = 0x3f;
  450.   while (!CTRL_QUERY) {
  451.     if (!--timeout)
  452.       return;
  453.   }
  454.   goto read_loop;
  455.  
  456. }
RAW Paste Data
Ledger Nano X - The secure hardware wallet
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Top