Memory efficient reception code (one bit per bit!)

This commit is contained in:
Raphael Assenat 2015-10-28 23:03:16 -04:00
parent d444b9479f
commit 1a44a190cb
6 changed files with 51 additions and 87 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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__

View File

@ -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
View File

@ -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;