/* gc_n64_usb : Gamecube or N64 controller to USB firmware Copyright (C) 2007-2015 Raphael Assenat This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "gcn64_protocol.h" #undef FORCE_KEYBOARD #define GCN64_BUF_SIZE 600 static volatile unsigned char gcn64_workbuf[GCN64_BUF_SIZE]; /******** IO port definitions and options **************/ #ifndef STK525 #define GCN64_DATA_PORT PORTD #define GCN64_DATA_DDR DDRD #define GCN64_DATA_PIN PIND #define GCN64_DATA_BIT (1<<0) #define GCN64_BIT_NUM_S "0" // for asm #define FREQ_IS_16MHZ #define DISABLE_INTS_DURING_COMM #else #define GCN64_DATA_PORT PORTA #define GCN64_DATA_DDR DDRA #define GCN64_DATA_PIN PINA #define GCN64_DATA_BIT (1<<0) #define GCN64_BIT_NUM_S "0" // for asm #define FREQ_IS_16MHZ #define DISABLE_INTS_DURING_COMM #endif /* * \brief Explode bytes to bits * \param bytes The input byte array * \param num_bytes The number of input bytes * \param workbuf_bit_offset The offset to start writing at * \return number of bits (i.e. written output bytes) * * 1 input byte = 8 output bytes, where each output byte is zero or non-zero depending * on the input byte bits, starting with the most significant one. */ static int bitsToWorkbufBytes(unsigned char *bytes, int num_bytes, int workbuf_bit_offset) { int i, bit; unsigned char p; if (num_bytes * 8 > GCN64_BUF_SIZE) return 0; for (i=0,bit=0; i>=1) { gcn64_workbuf[bit+workbuf_bit_offset] = bytes[i] & p; bit++; } } return bit; } /* Read a byte from the buffer (where 1 byte is 1 bit). * MSb first. */ unsigned char gcn64_protocol_getByte(int offset) { unsigned char val, b; unsigned char volatile *addr = gcn64_workbuf + offset; for (b=0x80, val=0; b; b>>=1) { if (*addr) val |= b; addr++; } return val; } void gcn64_protocol_getBytes(int offset, int n_bytes, unsigned char *dstbuf) { int i; for (i=0; i 127 (approx 50uS timeout) " sbic %2, "GCN64_BIT_NUM_S" \n" " rjmp waitlow_lp \n" " adiw %0, 1 \n" // count this timed low level " breq overflow \n" // > 255 " st z+,r16 \n" "waithigh:\n" " ldi r16, %4 \n" "waithigh_lp:\n" " inc r16 \n" " brmi timeout \n" // > 127 " sbis %2, "GCN64_BIT_NUM_S" \n" " rjmp waithigh_lp \n" " adiw %0, 1 \n" // count this timed high level " breq overflow \n" // > 255 " st z+,r16 \n" " rjmp waitlow \n" "overflow: \n" "timeout: \n" " pop r31 \n" // restore z " pop r30 \n" // restore z : "=&x" (count) // %0 : "z" ((unsigned char volatile *)gcn64_workbuf), // %1 "I" (_SFR_IO_ADDR(GCN64_DATA_PIN)), // %2 "I" (_SFR_IO_ADDR(PORTB)), // %3 "M" (TIMING_OFFSET) // %4 : "r16","memory" ); return count; } static void gcn64_sendBytes(unsigned char *data, unsigned char n_bytes) { unsigned int bits; if (n_bytes == 0) return; // Explode the data to one byte per bit for very easy transmission in assembly. // This trades memory for ease of implementation. bits = bitsToWorkbufBytes(data, n_bytes, 0); // the value of the gpio is pre-configured to low. We simulate // an open drain output by toggling the direction. #define PULL_DATA " sbi %0, "GCN64_BIT_NUM_S"\n" #define RELEASE_DATA " cbi %0, "GCN64_BIT_NUM_S"\n" #ifdef FREQ_IS_16MHZ // busy looping delays based on busy loop and nop tuning. // valid for 16Mhz clock. (Tuned to 1us/3us using a scope) #define DLY_SHORT_1ST "ldi r17, 2\n nop\nrcall sb_dly%=\n " #define DLY_LARGE_1ST "ldi r17, 13\n rcall sb_dly%=\n" #define DLY_SHORT_2ND "nop\nnop\nnop\nnop\n" #define DLY_LARGE_2ND "ldi r17, 9\n rcall sb_dly%=\nnop\nnop\n" #else // busy looping delays based on busy loop and nop tuning. // valid for 12Mhz clock. #define DLY_SHORT_1ST "ldi r17, 1\n rcall sb_dly%=\n " #define DLY_LARGE_1ST "ldi r17, 9\n rcall sb_dly%=\n" #define DLY_SHORT_2ND "\n" #define DLY_LARGE_2ND "ldi r17, 5\n rcall sb_dly%=\n nop\nnop\n" #endif asm volatile( // Save the modified input operands " push r28 \n" // y " push r29 \n" " push r30 \n" // z " push r31 \n" "sb_loop%=: \n" " ld r16, z+ \n" " tst r16 \n" " breq sb_send0%= \n" " brne sb_send1%= \n" " rjmp sb_end%= \n" // not reached "sb_send0%=: \n" " nop \n" PULL_DATA DLY_LARGE_1ST RELEASE_DATA DLY_SHORT_2ND " sbiw %1, 1 \n" " brne sb_loop%= \n" " rjmp sb_end%= \n" "sb_send1%=: \n" PULL_DATA DLY_SHORT_1ST RELEASE_DATA DLY_LARGE_2ND " sbiw %1, 1 \n" " brne sb_loop%= \n" " rjmp sb_end%= \n" // delay sub (arg r17) "sb_dly%=: \n" " dec r17 \n" " brne sb_dly%= \n" " ret \n" "sb_end%=:\n" // going here is fast so we need to extend the last // delay by 500nS " nop\n " #ifdef FREQ_IS_16MHZ " nop\n" #endif " pop r31 \n" " pop r30 \n" PULL_DATA " pop r29 \n" " pop r28 \n" //DLY_SHORT_1ST "nop\nnop\nnop\nnop\n" RELEASE_DATA // Now, we need to loop until the wire is high to // prevent the reception code from thinking this is // the beginning of the first reply bit. " ldi r16, 0xff \n" // setup a timeout "sb_waitHigh%=: \n" " dec r16 \n" // decrement timeout " breq sb_wait_high_done%= \n" // handle timeout condition " sbis %3, "GCN64_BIT_NUM_S" \n" // Read the port " rjmp sb_waitHigh%= \n" "sb_wait_high_done%=:\n" : : "I" (_SFR_IO_ADDR(GCN64_DATA_DDR)), // %0 "w" (bits), // %1 "z" ((unsigned char volatile *)gcn64_workbuf), // %2 "I" (_SFR_IO_ADDR(GCN64_DATA_PIN)) // %3 : "r16", "r17"); } /* \brief Decode the received length of low/high states to byte-per-bit format * * The result is in workbuf. * **/ static void gcn64_decodeWorkbuf(unsigned int count) { unsigned int i; volatile unsigned char *output = gcn64_workbuf; volatile unsigned char *input = gcn64_workbuf; unsigned char t; // // ________ // ________/ // // [i*2] [i*2+1] // // ________________ // 0 : ____/ // ____ // 1 : ________________/ // // The timings on a real N64 are // // 0 : 1 us low, 3 us high // 1 : 3 us low, 1 us high // // However, HORI pads use something similar to // // 0 : 1.5 us low, 4.5 us high // 1 : 4.5 us low, 1.5 us high // // // No64 us = microseconds // This operation takes approximately 100uS on 64bit gamecube messages for (i=0; i> 8) { case 0x05: return CONTROLLER_IS_N64; case 0x09: // normal controllers case 0x0b: // Never saw this one, but it is mentionned above. return CONTROLLER_IS_GC; case 0x08: if (id == 0x0820) { // Ascii keyboard return CONTROLLER_IS_GC_KEYBOARD; } // wavebird, controller off. return CONTROLLER_IS_GC; default: return CONTROLLER_IS_UNKNOWN; } return 0; }