gc_n64_usb-v3/gcn64txrx.S

265 lines
5.3 KiB
ArmAsm

#include <avr/io.h>
; When compiling, you must define the following.
;
; SUFFIX : The suffix for exported function (eg: Set to 0 to generate gcn64_sendBytes0)
; GCN64_DATA_BIT : The bit number in the port.
;
; Port and Pin IOs are defined below.
;
#define CONCAT(a,b) a##b
#define EXPORT_SYMBOL(a, b) .global CONCAT(a,b)
#define FUNCTION(a, b) CONCAT(a,b)
.text
EXPORT_SYMBOL(gcn64_sendBytes, SUFFIX)
EXPORT_SYMBOL(gcn64_receiveBytes, SUFFIX)
#define xl r26
#define xh r27
#define yl r28
#define yh r29
#define zl r30
#define zh r31
#define __zero_reg__ r1
/* 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
/************************************************
* Function gcn64_receiveBits
*
* Works by timing the low and high periods of each bit and
* then comparing them to know if a 1 or a 0 was on the wire.
*
* Example timed values:
* 54 4e 53 4d 53 4d 53 4e 53 4d 4d 53 53 4e 4c 54 53 4d 54 4d 53 4d 53 4d ...
* 0 0 0 0 0 1 0 1 0 0 0 0 ....
*/
; 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 (0xff: Error, 0xfe: Overflow [max_bytes too low])
FUNCTION(gcn64_receiveBytes, SUFFIX):
clr xl
clr xh
mov zl, r24
mov zh, r25
clr r18
ldi r20, 1
clr r24
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:
mov r21, r18 ; Save the last value
ldi r18, TIMING_OFFSET
waitlow_lp:
inc r18
brmi rxdone ; > 127 (approx 50uS timeout)
sbic GCN64_DATA_PIN, GCN64_DATA_BIT
rjmp waitlow_lp
; Compare the low period and the high period.
sub r19, r18 ; Carry is set when 1
rol r20
brcs store_byte
rjmp waithigh
store_byte:
cp r22, r24 ; Check max_bytes
breq overflow
inc r24 ; Count byte
st z+,r20
ldi r20, 1
waithigh:
ldi r19, TIMING_OFFSET
waithigh_lp:
inc r19
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
overflow:
ser r24 ; 0xff
dec r24 ; 0xfe
ret
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:
ser r24
rxdone:
; Return the number if received bits in r24
ret
; These are for a slower 4us/1.5us timing.
; The MadCatz Microcon does not work with 3us/1us timing...
#define LOOPS_SEND0_LOW 20
#define DELAY_SEND0_HIGH 2
#define LOOPS_SEND1_LOW 4
#define LOOPS_SEND1_HIGH 13
/*
; These are for the perfect 3us/1us timing described below
#define LOOPS_SEND0_LOW 15
#define LOOPS_SEND1_LOW 4
#define LOOPS_SEND1_HIGH 10
*/
/************************************************
* 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.
*
************************************************/
FUNCTION(gcn64_sendBytes, SUFFIX):
; 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, LOOPS_SEND0_LOW
lp_send0_3us:
dec r20
brne lp_send0_3us
nop
cbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Release bus to 1
#ifdef DELAY_SEND0_HIGH
ldi r20, DELAY_SEND0_HIGH
lp_send0_1us:
dec r20
brne lp_send0_1us
#endif
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, LOOPS_SEND1_LOW
lp_send1_1us:
dec r20
brne lp_send1_1us
nop
nop
cbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Release bus to 1
ldi r20, LOOPS_SEND1_HIGH
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, LOOPS_SEND1_LOW
stbdly0:
dec r20
brne stbdly0
nop
cbi GCN64_DATA_DDR, GCN64_DATA_BIT ; Release
done_send:
ret