mirror of
https://github.com/raphnet/gc_n64_usb-v3
synced 2024-12-21 23:08:53 -05:00
Memory efficient reception code (one bit per bit!)
This commit is contained in:
parent
d444b9479f
commit
1a44a190cb
@ -14,16 +14,18 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gcn64_protocol.h"
|
||||
#include "gcn64txrx.h"
|
||||
|
||||
#undef FORCE_KEYBOARD
|
||||
|
||||
#define GCN64_BUF_SIZE 320 // Supports up to 39 bytes
|
||||
#define GCN64_BUF_SIZE 40
|
||||
static unsigned char gcn64_workbuf[GCN64_BUF_SIZE];
|
||||
|
||||
/******** IO port definitions and options **************/
|
||||
@ -49,26 +51,12 @@ static unsigned char gcn64_workbuf[GCN64_BUF_SIZE];
|
||||
*/
|
||||
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;
|
||||
return gcn64_workbuf[offset/8];
|
||||
}
|
||||
|
||||
void gcn64_protocol_getBytes(int offset, int n_bytes, unsigned char *dstbuf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<n_bytes; i++) {
|
||||
*dstbuf = gcn64_protocol_getByte(offset + (i*8));
|
||||
dstbuf++;
|
||||
}
|
||||
memcpy(dstbuf, gcn64_workbuf + offset/8, n_bytes);
|
||||
}
|
||||
|
||||
void gcn64protocol_hwinit(void)
|
||||
@ -87,22 +75,21 @@ void gcn64protocol_hwinit(void)
|
||||
|
||||
/**
|
||||
* \brief Send n data bytes + stop bit, wait for answer.
|
||||
* \return The number of bits received, 0 on timeout/error.
|
||||
* \return The number of bytes received, 0 on timeout/error.
|
||||
*
|
||||
* The result is in gcn64_workbuf, where each byte represents
|
||||
* a bit.
|
||||
* The result is in gcn64_workbuf.
|
||||
*/
|
||||
int gcn64_transaction(unsigned char *data_out, int data_out_len)
|
||||
{
|
||||
int count;
|
||||
unsigned char sreg = SREG;
|
||||
int i;
|
||||
// int i;
|
||||
|
||||
#ifdef DISABLE_INTS_DURING_COMM
|
||||
cli();
|
||||
#endif
|
||||
gcn64_sendBytes(data_out, data_out_len);
|
||||
count = gcn64_receiveBits(gcn64_workbuf, 0);
|
||||
count = gcn64_receiveBytes(gcn64_workbuf, 0);
|
||||
SREG = sreg;
|
||||
#if 0
|
||||
printf("Count: %d { ", count);
|
||||
@ -114,23 +101,17 @@ int gcn64_transaction(unsigned char *data_out, int data_out_len)
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (!(count & 0x01)) {
|
||||
// If we don't get an odd number of level lengths from gcn64_receive
|
||||
// something is wrong.
|
||||
//
|
||||
// The stop bit is a short (~1us) low state followed by an "infinite"
|
||||
// high state, which timeouts and lets the function return. This
|
||||
// is why we should receive and odd number of lengths.
|
||||
if (count == 0xff) {
|
||||
printf("rx error\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this delay is required on N64 controllers. Otherwise, after sending
|
||||
* a rumble-on or rumble-off command (probably init too), the following
|
||||
* get status fails. This starts to work at 30us. 60us should be safe. */
|
||||
_delay_us(60); // Note that this results in a 100us delay between packets.
|
||||
_delay_us(80); // Note that this results in a ~100us delay between packets.
|
||||
|
||||
/* return the number of full bits received, minus the stop bit */
|
||||
return count-1;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@ -140,14 +121,14 @@ int gcn64_transaction(unsigned char *data_out, int data_out_len)
|
||||
int gcn64_detectController(void)
|
||||
{
|
||||
unsigned char tmp = GC_GETID;
|
||||
int count;
|
||||
unsigned char count;
|
||||
unsigned short id;
|
||||
|
||||
count = gcn64_transaction(&tmp, 1);
|
||||
if (count == 0) {
|
||||
return CONTROLLER_IS_ABSENT;
|
||||
}
|
||||
if (count != 24) {
|
||||
if (count != GC_GETID_REPLY_LENGTH) {
|
||||
return CONTROLLER_IS_UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -11,14 +11,14 @@
|
||||
/* Return many unknown bits, but two are about the expansion port. */
|
||||
#define N64_GET_CAPABILITIES 0x00
|
||||
#define N64_RESET 0xFF
|
||||
#define N64_CAPS_REPLY_LENGTH 24
|
||||
#define N64_CAPS_REPLY_LENGTH 3
|
||||
|
||||
#define OFFSET_EXT_REMOVED 22
|
||||
#define OFFSET_EXT_PRESENT 23
|
||||
|
||||
/* Returns button states and axis values */
|
||||
#define N64_GET_STATUS 0x01
|
||||
#define N64_GET_STATUS_REPLY_LENGTH 32
|
||||
#define N64_GET_STATUS_REPLY_LENGTH 4
|
||||
|
||||
/* Read from the expansion bus. */
|
||||
#define N64_EXPANSION_READ 0x02
|
||||
@ -28,14 +28,14 @@
|
||||
|
||||
/* Return information about controller. */
|
||||
#define GC_GETID 0x00
|
||||
#define GC_GETID_REPLY_LENGTH 24
|
||||
#define GC_GETID_REPLY_LENGTH 3
|
||||
|
||||
/* 3-byte get status command. Returns axis and buttons. Also
|
||||
* controls motor. */
|
||||
#define GC_GETSTATUS1 0x40
|
||||
#define GC_GETSTATUS2 0x03
|
||||
#define GC_GETSTATUS3(rumbling) ((rumbling) ? 0x01 : 0x00)
|
||||
#define GC_GETSTATUS_REPLY_LENGTH 64
|
||||
#define GC_GETSTATUS_REPLY_LENGTH 8
|
||||
|
||||
/* 3-byte poll keyboard command.
|
||||
* Source: http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9.3.3
|
||||
|
63
gcn64txrx.S
63
gcn64txrx.S
@ -2,7 +2,7 @@
|
||||
|
||||
.text
|
||||
.global gcn64_sendBytes
|
||||
.global gcn64_receiveBits
|
||||
.global gcn64_receiveBytes
|
||||
|
||||
#define xl r26
|
||||
#define xh r27
|
||||
@ -50,14 +50,14 @@
|
||||
; r24,r25 : dstbuf
|
||||
; r22 : max bytes (for fututre use)
|
||||
; return: count in r24,r25
|
||||
gcn64_receiveBits:
|
||||
gcn64_receiveBytes:
|
||||
clr xl
|
||||
clr xh
|
||||
mov zl, r24
|
||||
mov zh, r25
|
||||
clr r18
|
||||
clr r20
|
||||
inc r20
|
||||
ldi r20, 1
|
||||
clr r24
|
||||
initial_wait_low:
|
||||
inc r18
|
||||
breq timeout ; overflow to 0
|
||||
@ -72,61 +72,40 @@ waitlow:
|
||||
ldi r18, TIMING_OFFSET
|
||||
waitlow_lp:
|
||||
inc r18
|
||||
brmi timeout_waitlow ; > 127 (approx 50uS timeout)
|
||||
brmi rxdone ; > 127 (approx 50uS timeout)
|
||||
sbic GCN64_DATA_PIN, GCN64_DATA_BIT
|
||||
rjmp waitlow_lp
|
||||
|
||||
adiw xl, 1 ; count this bit
|
||||
; TODO : Check maximum size
|
||||
;breq overflow ; > 255
|
||||
|
||||
#if 0
|
||||
st z+, r19
|
||||
st z+, r18
|
||||
rjmp waithigh
|
||||
#else
|
||||
; Compare the low period and the high period.
|
||||
sub r19, r18
|
||||
brcs got1
|
||||
got0:
|
||||
st z+, __zero_reg__
|
||||
sub r19, r18 ; Carry is set when 1
|
||||
rol r20
|
||||
brcs store_byte
|
||||
rjmp waithigh
|
||||
got1:
|
||||
|
||||
store_byte:
|
||||
inc r24 ; Count byte
|
||||
breq overflow ; TODO : Check against r22
|
||||
st z+,r20
|
||||
#endif
|
||||
ldi r20, 1
|
||||
|
||||
waithigh:
|
||||
ldi r19, TIMING_OFFSET
|
||||
waithigh_lp:
|
||||
inc r19
|
||||
brmi timeout ; > 127
|
||||
brmi frame_error ; This means the line is stuck in a low state...
|
||||
sbis GCN64_DATA_PIN, GCN64_DATA_BIT
|
||||
rjmp waithigh_lp
|
||||
rjmp waitlow
|
||||
|
||||
timeout_waitlow:
|
||||
adiw xl, 1 ;
|
||||
; TODO : Check maximum size
|
||||
;breq overflow ; > 255
|
||||
|
||||
; Compare the low period and the high period.
|
||||
sub r19, r21
|
||||
brcs lastwas1
|
||||
lastwas0:
|
||||
st z+, __zero_reg__
|
||||
rjmp rxdone
|
||||
lastwas1:
|
||||
st z+,r20
|
||||
rjmp rxdone
|
||||
|
||||
overflow:
|
||||
clr xl
|
||||
clr xh
|
||||
timeout:
|
||||
tst r24
|
||||
breq rxdone ; If r24 is still zero, we did not receive anything. Return 0.
|
||||
; Otherwise, it is a frame error (i.e. A partial byte was received)
|
||||
frame_error:
|
||||
overflow: ; Treat overflow as an error as well
|
||||
ser r24
|
||||
rxdone:
|
||||
; Return the number if received bits in r24,r25
|
||||
mov r24, xl ; yl
|
||||
mov r25, xh ; yh
|
||||
; Return the number if received bits in r24
|
||||
ret
|
||||
|
||||
|
||||
|
@ -2,6 +2,13 @@
|
||||
#define _gcn64txrx_h__
|
||||
|
||||
void gcn64_sendBytes(unsigned char *data, unsigned char n_bytes);
|
||||
unsigned int gcn64_receiveBits(unsigned char *dstbuf, unsigned char max_bits);
|
||||
|
||||
/**
|
||||
* \brief Receive up to \max_bytes bytes
|
||||
* \param dstbuf Destination buffer
|
||||
* \param max_bytes The maximum number of bytes
|
||||
* \return The number of received bytes. 0xFF in case of error
|
||||
*/
|
||||
unsigned char gcn64_receiveBytes(unsigned char *dstbuf, unsigned char max_bytes);
|
||||
|
||||
#endif // _gcn64txrx_h__
|
||||
|
@ -58,7 +58,6 @@ uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uin
|
||||
|
||||
static void hiddata_processCommandBuffer(void)
|
||||
{
|
||||
int bits;
|
||||
unsigned char channel;
|
||||
#ifdef DEBUG
|
||||
int i;
|
||||
@ -79,8 +78,7 @@ static void hiddata_processCommandBuffer(void)
|
||||
// TODO : Range checking
|
||||
// cmdbuf[] : RQ, CHN, LEN, data[]
|
||||
channel = cmdbuf[1];
|
||||
bits = gcn64_transaction(cmdbuf+3, cmdbuf[2]);
|
||||
cmdbuf_len = bits / 8; // The above return a number of bits
|
||||
cmdbuf_len = gcn64_transaction(cmdbuf+3, cmdbuf[2]);
|
||||
gcn64_protocol_getBytes(0, cmdbuf_len, cmdbuf + 3);
|
||||
cmdbuf[2] = cmdbuf_len;
|
||||
cmdbuf_len += 3; // Answer: RQ, CHN, LEN, data[]
|
||||
|
5
n64.c
5
n64.c
@ -59,9 +59,8 @@ static char initRumble(void)
|
||||
tmpdata[2] = 0x01;
|
||||
memset(tmpdata+3, 0x80, 32);
|
||||
|
||||
/* Note: The old test (count > 0) was not reliable. */
|
||||
count = gcn64_transaction(tmpdata, 35);
|
||||
if (count == 8)
|
||||
if (count == 1)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
@ -76,7 +75,7 @@ static char controlRumble(char enable)
|
||||
tmpdata[2] = 0x1b;
|
||||
memset(tmpdata+3, enable ? 0x01 : 0x00, 32);
|
||||
count = gcn64_transaction(tmpdata, 35);
|
||||
if (count == 8)
|
||||
if (count == 1)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
|
Loading…
Reference in New Issue
Block a user