/* This file contains assembly macros used
* in usb_interface.c. These are just too long
* to fit comfortably in that file.
*/
/* Remember, we have only the three states of the lines in USB:
* SEO: both D+ and D- low.
* - TX: 0x40
* - RX: 0x00
* D1: differential 1, D+ high and D- low (K).
* - TX: 0x06
* - RX: 0x80
* D0: differential 0, D+ low and D- high (J).
* - TX: 0x05
* - RX: 0x40
* Pinout of the mcu:
* - P0: D- (transmit)
* - P1: D+ (transmit)
* - P2: TX enable
* - P6: D- (receive)
* - P7: D+ (receive)
*/
/* Registers that get loaded:
* X: address of buffer
* Z: address of handshake
* %1: portx
* %2: pinx
* r3: buffer that we use when loading from memory.
* r7: data direction.
* r8: transfer type (control=0, interrupt=1)
* r10: holds the value 5, used for transmitting data.
* r11: holds the value of a NACK pid.
* r12: holds the value of a STALL pid.
* r16: length of token packet.
* r17: length of data packet.
* r20: temporary register for our use.
* r21: timeout counter.
*/
/* {{{ Sync packet macros */
// Sync: KJKJKJKK
/* {{{ Transmitting a sync */
// There are 8 free cycles after sending the sync.
// It uses 1 cycle before sending data.
#define SIE_SEND_SYNC_K_NOPS \
SIE_SEND_SYNC_K \
NOP8
#define SIE_SEND_SYNC_J_NOPS \
SIE_SEND_SYNC_J \
NOP8
#define SIE_SEND_SYNC_K \
"ldi r20, 0x06\n\t" \
"out %1, r20\n\t"
#define SIE_SEND_SYNC_J \
"ldi r20, 0x05\n\t" \
"out %1, r20\n\t"
#define SIE_SEND_SYNC \
SIE_SEND_SYNC_K_NOPS \
SIE_SEND_SYNC_J_NOPS \
SIE_SEND_SYNC_K_NOPS \
SIE_SEND_SYNC_J_NOPS \
SIE_SEND_SYNC_K_NOPS \
SIE_SEND_SYNC_J_NOPS \
SIE_SEND_SYNC_K_NOPS \
SIE_SEND_SYNC_K
/* }}} */
/* {{{ Receiving a sync */
#define SIE_RX_SYNC_K \
"sbis %2, 7\n\t" \
"rjmp .-6\n\t"
#define SIE_RX_SYNC_J \
"sbis %2, 6\n\t" \
"rjmp .-6\n\t"
#define SIE_RX_SYNC_FIRST \
"ldi r21, 20\n\t" \
"clz\n\t" \
"ldi r20, 0xff\n\t" \
"out %5, r20\n\t" \
".sie_begin_get_sync:\n\t" \
"dec r21\n\t" \
"sbic %2, 7\n\t" \
"rjmp .sie_done_sync\n\t" \
"sbic %2, 7\n\t" \
"rjmp .sie_done_sync\n\t" \
"breq .sie_done_sync\n\t" \
"sbic %2, 7\n\t" \
"rjmp .sie_done_sync\n\t" \
"sbis %2, 7\n\t" \
"rjmp .sie_begin_get_sync\n\t" \
".sie_done_sync:\n\t" \
"brne .+4\n\t" \
"jmp .sie_sync_timeout\n\t" \
"ldi r20, 0x00\n\t" \
"out %5, r20\n\t"
#define SIE_RX_SYNC_FIRST_ACK \
"ldi r21, 20\n\t" \
"clz\n\t" \
"ldi r20, 0xff\n\t" \
"out %5, r20\n\t" \
".sie_begin_get_sync_ack:\n\t" \
"dec r21\n\t" \
"sbic %2, 7\n\t" \
"rjmp .sie_done_sync_ack\n\t" \
"sbic %2, 7\n\t" \
"rjmp .sie_done_sync_ack\n\t" \
"breq .sie_done_sync_ack\n\t" \
"sbic %2, 7\n\t" \
"rjmp .sie_done_sync_ack\n\t" \
"sbis %2, 7\n\t" \
"rjmp .sie_begin_get_sync_ack\n\t" \
".sie_done_sync_ack:\n\t" \
"brne .+4\n\t" \
"jmp .sie_sync_timeout\n\t" \
"ldi r20, 0x00\n\t" \
"out %5, r20\n\t"
#define SIE_RX_SYNC \
"ldi r20, 0xff\n\t" \
"out %5, r20\n\t" \
"ldi r20, 0x00\n\t" \
"out %5, r20\n\t" \
SIE_RX_SYNC_J \
NOP2 \
"ldi r20, 0xff\n\t" \
"out %5, r20\n\t" \
"ldi r20, 0x00\n\t" \
"out %5, r20\n\t" \
SIE_RX_SYNC_K \
NOP8 \
SIE_RX_SYNC_J \
NOP2 \
"ldi r20, 0xff\n\t" \
"out %5, r20\n\t" \
"ldi r20, 0x00\n\t" \
"out %5, r20\n\t" \
SIE_RX_SYNC_K \
NOP8 \
SIE_RX_SYNC_J \
NOP2 \
"ldi r20, 0xff\n\t" \
"out %5, r20\n\t" \
"ldi r20, 0x00\n\t" \
"out %5, r20\n\t" \
SIE_RX_SYNC_K \
NOP8 \
NOP8 \
NOP4
/* }}} */
/* }}} */
/* {{{ End-of-packet macros */
// 1 cycle before it sends data.
// 8 free cycles when it ends.
#define SIE_SEND_EOP_PULSE_SE0_NOPS \
SIE_SEND_EOP_PULSE_SE0 \
NOP8
#define SIE_SEND_EOP_PULSE_J_NOPS \
SIE_SEND_EOP_PULSE_J \
NOP8
#define SIE_SEND_EOP_PULSE_SE0 \
"ldi r20, 0x04\n\t" \
"out %1, r20\n\t"
#define SIE_SEND_EOP_PULSE_J \
"ldi r20, 0x05\n\t" \
"out %1, r20\n\t"
#define SIE_SEND_EOP \
SIE_SEND_EOP_PULSE_SE0_NOPS \
SIE_SEND_EOP_PULSE_SE0_NOPS \
SIE_SEND_EOP_PULSE_J
/* }}} */
/* {{{ Token macros */
/* Overview of sending each bit:
* 1. Copy the buffer to the temp register.
* 2. AND the temp register with 0x01.
* 3. ADD 5 to the temp.
* 4. Send this out on the port.
* 5. Shift the buffer right.
* 6. Subtract 1 from the count.
* 7-8. Branch over the next instruction if not 0.
* 8. Jump to the end.
* For bit 7:
* 9-10. Load the next byte into the buffer.
* Note that there is no looping here, to save cycles. The maximum number of bytes after
* bit stuffing that we will have to send is 6 bytes.
*/
/* {{{ Token: Send a whole byte */
#define SIE_TOKEN_BYTE \
SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT_BUFFER
/* }}} */
/* {{{ Token: Send a single bit */
#define SIE_TOKEN_BIT \
"mov r20, r3\n\t" \
"andi r20,0x01\n\t" \
"add r20, r10\n\t" \
"out %1, r20\n\t" \
"lsr r3\n\t" \
"subi r16, 1\n\t" \
"brne .+4\n\t" \
"jmp .sie_send_token_eop\n\t"
/* }}} */
/* {{{ Token: Send bit, and nop until next one */
#define SIE_TOKEN_BIT_NOP \
SIE_TOKEN_BIT \
NOP2
/* }}} */
/* {{{ Token: Send bit, and buffer another byte */
#define SIE_TOKEN_BIT_BUFFER \
SIE_TOKEN_BIT \
"ld r3, X+\n\t"
/* }}} */
/* }}} */
/* {{{ Data packet macros */
/* {{{ RX macros */
/* Receiving data on the line:
* 1. Read input on the port.
* 2. AND with 0xC0 to mask the high bits.
* 3. Branch over the next instruction if the Z flag is not set.
* 4. Jump to the EOP handler
* 5. AND with 0x80 to get the D+ line.
* 6. OR the result with the buffer.
* 7. Increment the data buffer length.
* This is then different per bit:
* Bits 0:6:
* 8. Shift buffer right.
* Bit 7:
* 8-9. Store buffer to X+.
* The theoretical max number of bytes is 10.
*/
/* {{{ Data: Receive a byte */
#define SIE_DATA_RX_BYTE \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_BUFFER
#define SIE_DATA_RX_SPECIAL_BYTE \
SIE_DATA_RX_SPECIAL_BIT \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT_BUFFER
/* }}} */
/* {{{ Data: Receive a single bit */
#define SIE_DATA_RX_BIT \
"in r20, %2\n\t" \
"andi r20, 0xC0\n\t" \
"brne .+4\n\t" \
"jmp .sie_rx_data_eop\n\t" \
"andi r20, 0x80\n\t" \
"or r3, r20\n\t" \
"inc r17\n\t"
/* }}} */
/* {{{ Data: (special byte) Receive a single bit, and get ready to handle a handshake. */
#define SIE_DATA_RX_SPECIAL_BIT \
"in r20, %2\n\t" \
"andi r20, 0xC0\n\t" \
"brne .+4\n\t" \
"jmp .sie_rx_data_first_eop\n\t" \
"andi r20, 0x80\n\t" \
"or r3, r20\n\t" \
"inc r17\n\t" \
"lsr r3\n\t" \
"out %5, r20\n\t" \
NOP1
/* }}} */
/* {{{ Data: Receive a low bit (bits 6:0) */
#define SIE_DATA_RX_BIT_LOW \
SIE_DATA_RX_BIT \
"lsr r3\n\t" \
"out %5, r20\n\t" \
NOP1
/* }}} */
/* {{{ Data: Receive the high bit, and store the buffer back to memory */
#define SIE_DATA_RX_BIT_BUFFER \
SIE_DATA_RX_BIT \
"st X+, r3\n\t" \
"out %5, r20\n\t"
/* }}} */
/* }}} */
/* }}} */
/* {{{ TX macros */
/* {{{ Data: Send a whole byte */
#define SIE_DATA_BYTE \
SIE_DATA_BIT_NOP \
SIE_DATA_BIT_NOP \
SIE_DATA_BIT_NOP \
SIE_DATA_BIT_NOP \
SIE_DATA_BIT_NOP \
SIE_DATA_BIT_NOP \
SIE_DATA_BIT_NOP \
SIE_DATA_BIT_BUFFER
/* }}} */
/* {{{ Data: Send a single bit */
#define SIE_DATA_BIT \
"mov r20, r3\n\t" \
"andi r20,0x01\n\t" \
"add r20, r10\n\t" \
"out %1, r20\n\t" \
"lsr r3\n\t" \
"subi r17, 1\n\t" \
"brne .+4\n\t" \
"jmp .sie_send_data_eop\n\t"
/* }}} */
/* {{{ Data: Send bit, and nop until next one */
#define SIE_DATA_BIT_NOP \
SIE_DATA_BIT \
NOP2
/* }}} */
/* {{{ Data: Send bit, and buffer another byte */
#define SIE_DATA_BIT_BUFFER \
SIE_DATA_BIT \
"ld r3, X+\n\t" \
NOP1
/* }}} */
/* }}} */
/* }}} */
/* {{{ Handshake macros */
/* {{{ Transmitting handshakes */
// 1 cycle taken before sending.
// 8 cycles free afterward.
#define SIE_HANDSHAKE_HIGHBIT_TX \
"ldi r20, 0x06\n\t" \
"out %1, r20\n\t" \
NOP8
#define SIE_HANDSHAKE_LOWBIT_TX \
"ldi r20, 0x05\n\t" \
"out %1,r20\n\t" \
NOP8
/* {{{ Send ACK pid */
#define SIE_HANDSHAKE_TX_ACK \
/* ACK is 0x4b */ \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX
/* }}} */
/* {{{ Send NACK pid */
#define SIE_HANDSHAKE_TX_NACK \
/* NACK is 0x5a */ \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_LOWBIT_TX \
SIE_HANDSHAKE_HIGHBIT_TX
/* }}} */
/* }}} */
/* {{{ Receiving handshakes */
// Starts receiving right away.
// No cycles free afterward.
#define SIE_HANDSHAKE_RX_BIT \
"in r20, %2\n\t" \
"andi r20, 0x80\n\t" \
"lsr r3\n\t" \
"or r3, r20\n\t" \
NOP6
#define SIE_HANDSHAKE_RX_BYTE \
SIE_HANDSHAKE_RX_BIT \
SIE_HANDSHAKE_RX_BIT \
SIE_HANDSHAKE_RX_BIT \
SIE_HANDSHAKE_RX_BIT \
SIE_HANDSHAKE_RX_BIT \
SIE_HANDSHAKE_RX_BIT \
SIE_HANDSHAKE_RX_BIT \
SIE_HANDSHAKE_RX_BIT
/* }}} */
/* }}} */
/* {{{ Macros to generate nop-like sequences */
#define NOP_INIT \
"rjmp .+2\n\t" \
".nop_function:\n\t" \
"ret\n\t"
#define NOP1 \
"nop\n\t"
#define NOP2 \
"ld __tmp_reg__, X /* 2 nops */\n\t"
#define NOP3 \
NOP2 \
NOP1
#define NOP4 \
"push __tmp_reg__ /* 2 nops */\n\t" \
"pop __tmp_reg__ /* 2 more nops*/\n\t"
#define NOP5 \
NOP2 \
NOP3
#define NOP6 \
NOP3 \
NOP3
#define NOP7 \
NOP3 \
NOP4
#define NOP8 \
"call .nop_function /* really 8 nops */\n\t"
/* }}} */ // macros for nops
/* {{{ Delay macro */
#define SIE_DELAY_4 \
"clr r20\n\t" \
"inc r20\n\t" \
"cpi r20, 10\n\t" \
"brne .-10\n\t" \
NOP1
/* }}} */