/* Name: usbdrvasm.S
- * Project: AVR USB driver
+ * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
* Author: Christian Starkjohann
* Creation Date: 2007-06-13
* Tabsize: 4
* Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
- * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt)
- * Revision: $Id$
+ * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
*/
/*
the file appropriate for the given clock rate.
*/
-#include "iarcompat.h"
-#ifndef __IAR_SYSTEMS_ASM__
- /* configs for io.h */
-# define __SFR_OFFSET 0
-# define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */
-# include <avr/io.h> /* for CPU I/O register definitions and vectors */
-#endif /* __IAR_SYSTEMS_ASM__ */
-#include "usbdrv.h" /* for common defs */
-
+#define __SFR_OFFSET 0 /* used by avr-libc's register definitions */
+#include "usbportability.h"
+#include "usbdrv.h" /* for common defs */
/* register names */
#define x1 r16
#define cnt r19
#define x3 r20
#define x4 r21
-#define bitcnt r22
+#define x5 r22
+#define bitcnt x5
#define phase x4
#define leap x4
/* Some assembler dependent definitions and declarations: */
#ifdef __IAR_SYSTEMS_ASM__
-
-# define nop2 rjmp $+2 /* jump to next instruction */
-# define XL r26
-# define XH r27
-# define YL r28
-# define YH r29
-# define ZL r30
-# define ZH r31
-# define lo8(x) LOW(x)
-# define hi8(x) ((x)>>8) /* not HIGH to allow XLINK to make a proper range check */
-
extern usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBufOffset
extern usbCurrentTok, usbRxLen, usbRxToken, usbTxLen
- extern usbTxBuf, usbMsgLen, usbTxLen1, usbTxBuf1, usbTxLen3, usbTxBuf3
+ extern usbTxBuf, usbTxStatus1, usbTxStatus3
+# if USB_COUNT_SOF
+ extern usbSofCount
+# endif
public usbCrc16
public usbCrc16Append
COMMON INTVEC
- ORG INT0_vect
- rjmp SIG_INTERRUPT0
+# ifndef USB_INTR_VECTOR
+ ORG INT0_vect
+# else /* USB_INTR_VECTOR */
+ ORG USB_INTR_VECTOR
+# undef USB_INTR_VECTOR
+# endif /* USB_INTR_VECTOR */
+# define USB_INTR_VECTOR usbInterruptHandler
+ rjmp USB_INTR_VECTOR
RSEG CODE
#else /* __IAR_SYSTEMS_ASM__ */
-# define nop2 rjmp .+0 /* jump to next instruction */
-
+# ifndef USB_INTR_VECTOR /* default to hardware interrupt INT0 */
+# ifdef INT0_vect
+# define USB_INTR_VECTOR INT0_vect // this is the "new" define for the vector
+# else
+# define USB_INTR_VECTOR SIG_INTERRUPT0 // this is the "old" vector
+# endif
+# endif
.text
- .global SIG_INTERRUPT0
- .type SIG_INTERRUPT0, @function
+ .global USB_INTR_VECTOR
+ .type USB_INTR_VECTOR, @function
.global usbCrc16
.global usbCrc16Append
-
#endif /* __IAR_SYSTEMS_ASM__ */
+
+#if USB_INTR_PENDING < 0x40 /* This is an I/O address, use in and out */
+# define USB_LOAD_PENDING(reg) in reg, USB_INTR_PENDING
+# define USB_STORE_PENDING(reg) out USB_INTR_PENDING, reg
+#else /* It's a memory address, use lds and sts */
+# define USB_LOAD_PENDING(reg) lds reg, USB_INTR_PENDING
+# define USB_STORE_PENDING(reg) sts USB_INTR_PENDING, reg
+#endif
+
+#define usbTxLen1 usbTxStatus1
+#define usbTxBuf1 (usbTxStatus1 + 1)
+#define usbTxLen3 usbTxStatus3
+#define usbTxBuf3 (usbTxStatus3 + 1)
+
+
;----------------------------------------------------------------------------
; Utility functions
;----------------------------------------------------------------------------
#endif
-; extern unsigned usbCrc16(unsigned char *data, unsigned char len);
-; data: r24/25
-; len: r22
+#if USB_USE_FAST_CRC
+
+; This implementation is faster, but has bigger code size
+; Thanks to Slawomir Fras (BoskiDialer) for this code and to Shay Green for
+; even further optimizations!
+; It implements the following C pseudo-code:
+; unsigned table(unsigned char x)
+; {
+; unsigned value;
+;
+; value = (unsigned)x << 6;
+; value ^= (unsigned)x << 7;
+; if(parity(x))
+; value ^= 0xc001;
+; return value;
+; }
+; unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen)
+; {
+; unsigned crc = 0xffff;
+;
+; while(argLen--)
+; crc = table(lo8(crc) ^ *argPtr++) ^ hi8(crc);
+; return ~crc;
+; }
+
+; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen);
+; argPtr r24+25 / r16+r17
+; argLen r22 / r18
+; temp variables:
+; byte r18 / r22
+; scratch r23
+; resCrc r24+r25 / r16+r17
+; ptr X / Z
+usbCrc16:
+ movw ptrL, argPtrL
+ ldi resCrcL, 0xFF
+ ldi resCrcH, 0xFF
+ clr bitCnt ; zero reg
+ rjmp usbCrc16LoopTest
+usbCrc16ByteLoop:
+ ld byte, ptr+
+ eor byte, resCrcL ; scratch is now 'x' in table()
+ mov scratch, byte ; compute parity of 'x'
+ swap byte
+ eor byte, scratch
+ mov resCrcL, byte
+ lsr byte
+ lsr byte
+ eor byte, resCrcL
+ inc byte
+ andi byte, 2 ; byte is now parity(x) << 1
+ cp bitCnt, byte ; c = (byte != 0), then put in high bit
+ ror scratch ; so that after xoring, shifting, and xoring, it gives
+ ror byte ; the desired 0xC0 with resCrcH
+ mov resCrcL, byte
+ eor resCrcL, resCrcH
+ mov resCrcH, scratch
+ lsr scratch
+ ror byte
+ eor resCrcH, scratch
+ eor resCrcL, byte
+usbCrc16LoopTest:
+ subi argLen, 1
+ brsh usbCrc16ByteLoop
+ com resCrcL
+ com resCrcH
+ ret
+
+#else /* USB_USE_FAST_CRC */
+
+; This implementation is slower, but has less code size
+;
+; extern unsigned usbCrc16(unsigned char *argPtr, unsigned char argLen);
+; argPtr r24+25 / r16+r17
+; argLen r22 / r18
; temp variables:
-; r18: data byte
-; r19: bit counter
-; r20/21: polynomial
-; r23: scratch
-; r24/25: crc-sum
-; r26/27=X: ptr
+; byte r18 / r22
+; bitCnt r19
+; poly r20+r21
+; scratch r23
+; resCrc r24+r25 / r16+r17
+; ptr X / Z
usbCrc16:
mov ptrL, argPtrL
mov ptrH, argPtrH
- ldi resCrcL, 0xff
- ldi resCrcH, 0xff
+ ldi resCrcL, 0
+ ldi resCrcH, 0
ldi polyL, lo8(0xa001)
ldi polyH, hi8(0xa001)
-crcByteLoop:
- subi argLen, 1
- brcs crcReady
+ com argLen ; argLen = -argLen - 1: modified loop to ensure that carry is set
+ ldi bitCnt, 0 ; loop counter with starnd condition = end condition
+ rjmp usbCrcLoopEntry
+usbCrcByteLoop:
ld byte, ptr+
- ldi bitCnt, 8
-crcBitLoop:
- mov scratch, byte
- eor scratch, resCrcL
- lsr resCrcH
+ eor resCrcL, byte
+usbCrcBitLoop:
+ ror resCrcH ; carry is always set here (see brcs jumps to here)
ror resCrcL
- lsr byte
- sbrs scratch, 0
- rjmp crcNoXor
+ brcs usbCrcNoXor
eor resCrcL, polyL
eor resCrcH, polyH
-crcNoXor:
- dec bitCnt
- brne crcBitLoop
- rjmp crcByteLoop
-crcReady:
- com resCrcL
- com resCrcH
+usbCrcNoXor:
+ subi bitCnt, 224 ; (8 * 224) % 256 = 0; this loop iterates 8 times
+ brcs usbCrcBitLoop
+usbCrcLoopEntry:
+ subi argLen, -1
+ brcs usbCrcByteLoop
+usbCrcReady:
ret
+; Thanks to Reimar Doeffinger for optimizing this CRC routine!
+
+#endif /* USB_USE_FAST_CRC */
; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len);
usbCrc16Append:
st ptr+, resCrcH
ret
+#undef argLen
+#undef argPtrL
+#undef argPtrH
+#undef resCrcL
+#undef resCrcH
+#undef ptrL
+#undef ptrH
+#undef ptr
+#undef byte
+#undef bitCnt
+#undef polyL
+#undef polyH
+#undef scratch
+
+
+#if USB_CFG_HAVE_MEASURE_FRAME_LENGTH
+#ifdef __IAR_SYSTEMS_ASM__
+/* Register assignments for usbMeasureFrameLength on IAR cc */
+/* Calling conventions on IAR:
+ * First parameter passed in r16/r17, second in r18/r19 and so on.
+ * Callee must preserve r4-r15, r24-r29 (r28/r29 is frame pointer)
+ * Result is passed in r16/r17
+ * In case of the "tiny" memory model, pointers are only 8 bit with no
+ * padding. We therefore pass argument 1 as "16 bit unsigned".
+ */
+# define resL r16
+# define resH r17
+# define cnt16L r30
+# define cnt16H r31
+# define cntH r18
+
+#else /* __IAR_SYSTEMS_ASM__ */
+/* Register assignments for usbMeasureFrameLength on gcc */
+/* Calling conventions on gcc:
+ * First parameter passed in r24/r25, second in r22/23 and so on.
+ * Callee must preserve r1-r17, r28/r29
+ * Result is passed in r24/r25
+ */
+# define resL r24
+# define resH r25
+# define cnt16L r24
+# define cnt16H r25
+# define cntH r26
+#endif
+# define cnt16 cnt16L
+
+; extern unsigned usbMeasurePacketLength(void);
+; returns time between two idle strobes in multiples of 7 CPU clocks
+.global usbMeasureFrameLength
+usbMeasureFrameLength:
+ ldi cntH, 6 ; wait ~ 10 ms for D- == 0
+ clr cnt16L
+ clr cnt16H
+usbMFTime16:
+ dec cntH
+ breq usbMFTimeout
+usbMFWaitStrobe: ; first wait for D- == 0 (idle strobe)
+ sbiw cnt16, 1 ;[0] [6]
+ breq usbMFTime16 ;[2]
+ sbic USBIN, USBMINUS ;[3]
+ rjmp usbMFWaitStrobe ;[4]
+usbMFWaitIdle: ; then wait until idle again
+ sbis USBIN, USBMINUS ;1 wait for D- == 1
+ rjmp usbMFWaitIdle ;2
+ ldi cnt16L, 1 ;1 represents cycles so far
+ clr cnt16H ;1
+usbMFWaitLoop:
+ in cntH, USBIN ;[0] [7]
+ adiw cnt16, 1 ;[1]
+ breq usbMFTimeout ;[3]
+ andi cntH, USBMASK ;[4]
+ brne usbMFWaitLoop ;[5]
+usbMFTimeout:
+#if resL != cnt16L
+ mov resL, cnt16L
+ mov resH, cnt16H
+#endif
+ ret
+
+#undef resL
+#undef resH
+#undef cnt16
+#undef cnt16L
+#undef cnt16H
+#undef cntH
+
+#endif /* USB_CFG_HAVE_MEASURE_FRAME_LENGTH */
;----------------------------------------------------------------------------
; Now include the clock rate specific code
;----------------------------------------------------------------------------
#ifndef USB_CFG_CLOCK_KHZ
-# define USB_CFG_CLOCK_KHZ 12000
+# ifdef F_CPU
+# define USB_CFG_CLOCK_KHZ (F_CPU/1000)
+# else
+# error "USB_CFG_CLOCK_KHZ not defined in usbconfig.h and no F_CPU set!"
+# endif
#endif
-#if USB_CFG_CLOCK_KHZ == 12000
-# include "usbdrvasm12.S"
-#elif USB_CFG_CLOCK_KHZ == 16000
-# include "usbdrvasm16.S"
-#elif USB_CFG_CLOCK_KHZ == 16500
-# include "usbdrvasm165.S"
-#else
-# error "USB_CFG_CLOCK_KHZ is not one of the supported rates!"
-#endif
+#if USB_CFG_CHECK_CRC /* separate dispatcher for CRC type modules */
+# if USB_CFG_CLOCK_KHZ == 18000
+# include "usbdrvasm18-crc.inc"
+# else
+# error "USB_CFG_CLOCK_KHZ is not one of the supported rates for USB_CFG_CHECK_CRC!"
+# endif
+#else /* USB_CFG_CHECK_CRC */
+# if USB_CFG_CLOCK_KHZ == 12000
+# include "usbdrvasm12.inc"
+# elif USB_CFG_CLOCK_KHZ == 12800
+# include "usbdrvasm128.inc"
+# elif USB_CFG_CLOCK_KHZ == 15000
+# include "usbdrvasm15.inc"
+# elif USB_CFG_CLOCK_KHZ == 16000
+# include "usbdrvasm16.inc"
+# elif USB_CFG_CLOCK_KHZ == 16500
+# include "usbdrvasm165.inc"
+# elif USB_CFG_CLOCK_KHZ == 18000
+# include "usbdrvasm18.inc"
+# elif USB_CFG_CLOCK_KHZ == 20000
+# include "usbdrvasm20.inc"
+# else
+# error "USB_CFG_CLOCK_KHZ is not one of the supported rates!"
+# endif
+#endif /* USB_CFG_CHECK_CRC */