diff --git a/Makefile.inc b/Makefile.inc index 3a8f4e1..df4c19e 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -1,2 +1,2 @@ -OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o usbstrings.o intervaltimer.o version.o +OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o usbstrings.o intervaltimer.o version.o gcn64txrx.o VERSIONSTR=\"3.0.0\" diff --git a/gcn64_protocol.c b/gcn64_protocol.c index efb5006..5195e60 100644 --- a/gcn64_protocol.c +++ b/gcn64_protocol.c @@ -19,11 +19,12 @@ #include #include "gcn64_protocol.h" +#include "gcn64txrx.h" #undef FORCE_KEYBOARD #define GCN64_BUF_SIZE 600 -static volatile unsigned char gcn64_workbuf[GCN64_BUF_SIZE]; +static unsigned char gcn64_workbuf[GCN64_BUF_SIZE]; /******** IO port definitions and options **************/ #ifndef STK525 @@ -32,45 +33,16 @@ static volatile unsigned char gcn64_workbuf[GCN64_BUF_SIZE]; #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; +#define DISABLE_INTS_DURING_COMM - 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. @@ -99,311 +71,6 @@ void gcn64_protocol_getBytes(int offset, int n_bytes, unsigned char *dstbuf) } } -// The bit timeout is a counter to 127. This is the -// start value. Counting from 0 takes hundreads of -// microseconds. Because of this, the reception function -// "hangs in there" much longer than necessary.. -#ifdef FREQ_IS_16MHZ -#define TIMING_OFFSET 75 -#else -#define TIMING_OFFSET 100 // gives about 12uS. Twice the expected maximum bit period. -#endif - -#if 1 -static unsigned int gcn64_receive() -{ - register unsigned int count=0; - -#define SET_DBG " nop\n" -#define CLR_DBG " nop\n" -//#define SET_DBG " sbi %3, 4 \n" -//#define CLR_DBG " cbi %3, 4 \n" - - // The data line has been released. - // The receive part below expects it to be still high - // and will wait for it to become low before beginning - // the counting. - asm volatile( - " push r30 \n" // save Z - " push r31 \n" // save Z - " clr r27 \n" // clear X (%0) - " clr r26 \n" - - " clr r16 \n" -"initial_wait_low:\n" - " inc r16 \n" - " breq timeout \n" // overflow to 0 - " sbic %2, "GCN64_BIT_NUM_S" \n" - " rjmp initial_wait_low \n" - - // the next transition is to a high bit - " rjmp waithigh \n" - -"waitlow:\n" - " ldi r16, %4 \n" -"waitlow_lp:\n" - " inc r16 \n" - " brmi timeout \n" // > 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; -} -#endif - -static void gcn64_sendBytes(unsigned char *data, unsigned char n_bytes) -{ - - // 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" - - asm volatile( - "mov r23, %2 \n" - "tst r23 \n" - "breq done_send \n" - -"send_next_byte: \n" - "; Check if this is the last byte. \n" - "tst r23 \n" - "breq send_stop \n" - "dec r23 \n" - "ld r16, z+ \n" - "ldi r17, 0x80 ; mask \n" - -"send_next_bit: \n" - "mov r19, r16 \n" - "and r19, r17 \n" - "brne send1 \n" - "nop \n" - -"send0: \n" - PULL_DATA -// "sbi IO_DDRD, DATA_BIT ; Pull bus to 0 \n" - - "ldi r20, 15 \n" -"lp_send0_3us: \n" - "dec r20 \n" - "brne lp_send0_3us \n" - "nop \n" - - RELEASE_DATA -// "cbi IO_DDRD, DATA_BIT ; Release bus to 1 \n" - "lsr r17 \n" - "breq send_next_byte \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "rjmp send_next_bit \n" - -"send1: \n" - PULL_DATA -// "sbi IO_DDRD, DATA_BIT ; Pull bus to 0 \n" - - "ldi r20, 4 \n" -"lp_send1_1us: \n" - "dec r20 \n" - "brne lp_send1_1us \n" - "nop \n" - "nop \n" - - RELEASE_DATA -// "cbi IO_DDRD, DATA_BIT ; Release bus to 1 \n" - - "ldi r20, 10 \n" -"lp_send1_3us: \n" - "dec r20 \n" - "brne lp_send1_3us \n" - "nop \n" - "nop \n" - - "lsr r17 \n" - "breq send_next_byte \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "rjmp send_next_bit \n" - -"send_stop: \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - "nop \n" - -" ; STOP BIT \n" - PULL_DATA -//" sbi IO_DDRD, DATA_BIT ; Pull low for stop bit \n" -" ldi r20, 4 \n" -"stbdly0: \n" -" dec r20 \n" -" brne stbdly0 \n" -" nop \n" - RELEASE_DATA -//" cbi IO_DDRD, DATA_BIT ; Release \n" - -"done_send: \n" - : - : "I" (_SFR_IO_ADDR(GCN64_DATA_DDR)), // %0 - "z" ((unsigned char volatile *)gcn64_workbuf), // %1 (Z) - "r" (n_bytes) // %2 - : "r19","r16","r17","r20","r23" ); - -} - -#if 0 -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"); -} -#endif - /* \brief Decode the received length of low/high states to byte-per-bit format * * The result is in workbuf. @@ -484,7 +151,8 @@ int gcn64_transaction(unsigned char *data_out, int data_out_len) cli(); #endif gcn64_sendBytes(data_out, data_out_len); - count = gcn64_receive(); + //count = gcn64_receive(); + count = gcn64_receiveBits(gcn64_workbuf, 0); SREG = sreg; if (!count) @@ -612,5 +280,3 @@ int gcn64_detectController(void) return 0; } - - diff --git a/gcn64txrx.S b/gcn64txrx.S new file mode 100644 index 0000000..e8a19a9 --- /dev/null +++ b/gcn64txrx.S @@ -0,0 +1,208 @@ +#include + +.text +.global gcn64_sendBytes +.global gcn64_receiveBits + +#define xl r26 +#define xh r27 +#define yl r28 +#define yh r29 +#define zl r30 +#define zh r31 + +/* the value of the gpio is pre-configured to low. We simulate + an open drain output by toggling the direction */ + +#ifdef STK525 + #define GCN64_DATA_PORT _SFR_IO_ADDR(PORTA) + #define GCN64_DATA_DDR _SFR_IO_ADDR(DDRA) + #define GCN64_DATA_PIN _SFR_IO_ADDR(PINA) +#else + #define GCN64_DATA_PORT _SFR_IO_ADDR(PORTD) + #define GCN64_DATA_DDR _SFR_IO_ADDR(DDRD) + #define GCN64_DATA_PIN _SFR_IO_ADDR(PIND) +#endif +#define GCN64_DATA_BIT 0 + +#if F_CPU != 16000000L +#error Only 16MHz clock supported +#endif + + ; The bit timeout is a counter to 127. This is the + ; start value. Counting from 0 takes hundreads of + ; microseconds. Because of this, the reception function + ; "hangs in there" much longer than necessary.. +#define TIMING_OFFSET 75 + + ; unsigned int gcn64_receiveBytes(unsigned char *dstbuf, unsigned char max_bytes); + ; r24,r25 : dstbuf + ; r22 : max bytes (for fututre use) + ; return: count in r24,r25 +gcn64_receiveBits: + clr xl + clr xh + mov zl, r24 + mov zh, r25 + + clr r18 +initial_wait_low: + inc r18 + breq timeout ; overflow to 0 + sbic GCN64_DATA_PIN, GCN64_DATA_BIT + rjmp initial_wait_low + + ; the next transition is to a high bit + rjmp waithigh + +waitlow: + ldi r18, TIMING_OFFSET +waitlow_lp: + inc r18 + brmi timeout ; > 127 (approx 50uS timeout) + sbic GCN64_DATA_PIN, GCN64_DATA_BIT + rjmp waitlow_lp + + adiw xl, 1 ; count this timed low level + breq overflow ; > 255 + st z+,r18 + +waithigh: + ldi r18, TIMING_OFFSET +waithigh_lp: + inc r18 + brmi timeout ; > 127 + sbis GCN64_DATA_PIN, GCN64_DATA_BIT + rjmp waithigh_lp + adiw xl, 1 ; count this timed high level + + breq overflow ; > 255 + st z+,r18 + + rjmp waitlow + +overflow: +timeout: + ; Return the number if received bits in r24,r25 + mov r24, xl ; yl + mov r25, xh ; yh + ret + + + /************************************************ + * Send data using the N64/GC serial protocol which + * is as follows: + * 0 1 + * __ _____ + * ____| __| + * ^ ^ ^ ^ ^ ^ + * 3us 1us 1us 3us + * + * To send a 1, the pin direction is set to input. + * To send a 0, the pin direction is set to output. + * (of course, it's value is preset to zero) + * + * At 16 mhz, a 1us period is 16 cycles. Thus a 3us period + * is 48 cycles. + * + * Pointer to data is passed in r24, r25 + * Number of bytes to send is passed in r22 + * + * A stop bit is added at thy end of the packet. + * + ************************************************/ +gcn64_sendBytes: + ; Move r23,r24 pointer to z + mov zl, r24 + mov zh, r25 + + tst r22 + breq done_send + +send_next_byte: + ; Check if this is the last byte. + tst r22 + breq send_stop + dec r22 + ld r21, z+ + ldi r27, 0x80 ; mask + +send_next_bit: + mov r19, r21 + and r19, r27 + brne send1 + nop + +send0: + sbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Pull bus to 0 + + ldi r20, 15 +lp_send0_3us: + dec r20 + brne lp_send0_3us + nop + + cbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Release bus to 1 + + + lsr r27 + breq send_next_byte + nop + nop + nop + nop + nop + nop + rjmp send_next_bit + +send1: + sbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Pull bus to 0 + + ldi r20, 4 +lp_send1_1us: + dec r20 + brne lp_send1_1us + nop + nop + + cbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Release bus to 1 + + ldi r20, 10 +lp_send1_3us: + dec r20 + brne lp_send1_3us + nop + nop + + lsr r27 + breq send_next_byte + nop + nop + nop + nop + nop + nop + rjmp send_next_bit + +send_stop: + nop + nop + nop + nop + nop + nop + nop + nop + ; STOP BIT + sbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Pull low for stop bit + ldi r20, 4 +stbdly0: + dec r20 + brne stbdly0 + nop + cbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Release + +done_send: + ret + + diff --git a/gcn64txrx.h b/gcn64txrx.h new file mode 100644 index 0000000..4d27e46 --- /dev/null +++ b/gcn64txrx.h @@ -0,0 +1,7 @@ +#ifndef _gcn64txrx_h__ +#define _gcn64txrx_h__ + +void gcn64_sendBytes(unsigned char *data, unsigned char n_bytes); +unsigned int gcn64_receiveBits(unsigned char *dstbuf, unsigned char max_bits); + +#endif // _gcn64txrx_h__