/* Name: usbdrvasm.asm * Project: AVR USB driver * Author: Christian Starkjohann * Creation Date: 2006-03-01 * Tabsize: 4 * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH * License: GNU GPL v2 (see License.txt) or proprietary (CommercialLicense.txt) * This Revision: $Id$ */ /* General Description: The IAR compiler/assembler system prefers assembler files with file extension ".asm". We simply provide this file as an alias for usbdrvasm.S. Thanks to Oleg Semyonov for his help with the IAR tools port! */ /* Name: usbdrvasm.S * Project: AVR USB driver * 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$ */ /* General Description: This module is the assembler part of the USB driver. This file contains general code (preprocessor acrobatics and CRC computation) and then includes 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 /* for CPU I/O register definitions and vectors */ #endif /* __IAR_SYSTEMS_ASM__ */ #include "usbdrv.h" /* for common defs */ CRC5_TABLE EQU 0x1D00 /* register names */ #define x1 r16 #define x2 r17 #define shift r18 #define cnt r19 #define x3 r20 #define x4 r21 #define x5 r22 #define crclo x4 #define crchi x5 /* 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 public usbCrc16 public usbCrc16Append COMMON INTVEC ORG INT0_vect rjmp SIG_INTERRUPT0 RSEG CODE #else /* __IAR_SYSTEMS_ASM__ */ # define nop2 rjmp .+0 /* jump to next instruction */ .text .global SIG_INTERRUPT0 .type SIG_INTERRUPT0, @function .global usbCrc16 .global usbCrc16Append #endif /* __IAR_SYSTEMS_ASM__ */ ;---------------------------------------------------------------------------- ; Utility functions ;---------------------------------------------------------------------------- #ifdef __IAR_SYSTEMS_ASM__ /* Register assignments for usbCrc16 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". */ RTMODEL "__rt_version", "3" /* The line above will generate an error if cc calling conventions change. * The value "3" above is valid for IAR 4.10B/W32 */ # define argLen r18 /* argument 2 */ # define argPtrL r16 /* argument 1 */ # define argPtrH r17 /* argument 1 */ # define resCrcL r16 /* result */ # define resCrcH r17 /* result */ # define ptrL ZL # define ptrH ZH # define ptr Z # define scratchL r20 # define scratchH r21 ; extern unsigned usbCrc16(unsigned char *data, unsigned char len); ; data: r24/25 ; len: r22 ; temp variables: ; r18: data byte ; r19: bit counter ; r20/21: polynomial ; r23: scratch ; r24/25: crc-sum ; r26/27=X: ptr usbCrc16: movw scratchH:scratchL,XH:XL movw XH:XL,argPtrH:argPtrL ldi ptrH,CRC16_TABLE >> 8 ldi resCrcL, 0xff ldi resCrcH, 0xff tst argLen breq crcReady crcByteLoop: ld ptrL,X+ eor ptrL,resCrcL lpm resCrcL,ptr inc ptrH eor resCrcL,resCrcH lpm resCrcH,ptr dec ptrH dec argLen brne crcByteLoop crcReady: movw ptrH:ptrL,XH:XL movw XH:XL,scratchH:scratchL com resCrcL com resCrcH ret ; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); usbCrc16Append: rcall usbCrc16 st ptr+, resCrcL st ptr+, resCrcH ret #else /* __IAR_SYSTEMS_ASM__ */ /* Register assignments for usbCrc16 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 argLen r22 /* argument 2 */ # define argPtrL r24 /* argument 1 */ # define argPtrH r25 /* argument 1 */ # define resCrcL r24 /* result */ # define resCrcH r25 /* result */ # define ptrL XL # define ptrH XH # define ptr x # define byte r18 # define bitCnt r19 # define polyL r20 # define polyH r21 # define scratch r23 ; extern unsigned usbCrc16(unsigned char *data, unsigned char len); ; data: r24/25 ; len: r22 ; temp variables: ; r18: data byte ; r19: bit counter ; r20/21: polynomial ; r23: scratch ; r24/25: crc-sum ; r26/27=X: ptr usbCrc16: mov ptrL, argPtrL mov ptrH, argPtrH ldi resCrcL, 0xff ldi resCrcH, 0xff ldi polyL, lo8(0xa001) ldi polyH, hi8(0xa001) crcByteLoop: subi argLen, 1 brcs crcReady ld byte, ptr+ ldi bitCnt, 8 crcBitLoop: mov scratch, byte eor scratch, resCrcL lsr resCrcH ror resCrcL lsr byte sbrs scratch, 0 rjmp crcNoXor eor resCrcL, polyL eor resCrcH, polyH crcNoXor: dec bitCnt brne crcBitLoop rjmp crcByteLoop crcReady: com resCrcL com resCrcH ret ; extern unsigned usbCrc16Append(unsigned char *data, unsigned char len); usbCrc16Append: rcall usbCrc16 st ptr+, resCrcL st ptr+, resCrcH ret #endif SIG_INTERRUPT0: ;order of registers pushed: r0, SREG [sofError], YL, YH, shift, x1, x2, x3, x4, cnt push r0 ;[-23] push only what is necessary to sync with edge ASAP in r0, SREG ;[-21] push r0 ;[-20] ;---------------------------------------------------------------------------- ; Synchronize with sync pattern: ;---------------------------------------------------------------------------- ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] ;sync up with J to K edge during sync pattern -- use fastest possible loops ;first part has no timeout because it waits for IDLE or SE1 (== disconnected) waitForJ: sbis USBIN, USBMINUS ;[-18] wait for D- == 1 rjmp waitForJ waitForK: ;The following code results in a sampling window of < 1/4 bit which meets the spec. sbis USBIN, USBMINUS ;[001] rjmp foundK ;[002] sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK rjmp sofError foundK: ;[003] ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets ;are cycles from center of first sync (double K) bit after the instruction push YL ;[004] ; [---] ;[005] push YH ;[006] ; [---] ;[007] lds YL,usbInputBufOffset;[008] ; [---] ;[009] clr YH ;[010] subi YL, lo8(-(usbRxBuf));[011] [rx loop init] sbci YH, (hi8(-(usbRxBuf))) & 0xFF;[012] [rx loop init] sbis USBIN, USBMINUS ;[000] we want two bits K (sample 2 cycles too early) rjmp haveTwoBitsK ;[001] pop YH ;[002] undo the pushes from before pop YL ;[003] rjmp waitForK ;[004] this was not the end of sync, retry ; The entire loop from waitForK until rjmp waitForK above must not exceed two ; bit times (= 22 cycles). ;---------------------------------------------------------------------------- ; push more registers and initialize values while we sample the first bits: ;---------------------------------------------------------------------------- haveTwoBitsK: ;[002] push x1 ;[003] ; [---] ;[004] push x2 ;[005] ; [---] ;[006] push x3 ;[007] ; [---] ;[008] push shift ;[009] ; [---] ;[010] push ZH ;[011] ; [---] ;[012] ldi shift,0xFF ;[013] ;------------------------------------------ ;ZL = crclo^byte ;ZH - base ;crchi - crchi ; ; later ; crclo=lpm Z ; crclo^=crchi ; crchi=lpm Z+0x100 ; ; for CRC5 crclo=0x1F; init ZL=5 (crchi clear automaticaly) ; for CRC16 crclo=0xFF, crchi=0xFF; init ZL=0x54, crchi=0xFE (crchi=tab[0x154], crclo=tab[0x54]^0xFE) ;------------------------------------------ in x1, USBIN ;[000] <-- sample bit 0 bst x1, USBMINUS ;[001] bld shift, 0 ;[002] push x4 ;[003] == phase ; [---] ;[004] push cnt ;[005] ; [---] ;[006] push x5 ;[007] ; [---] ;[008] ldi cnt, USB_BUFSIZE;[009] [rx loop init] push ZL ;[010] ; [---] ;[011] ldi ZL,5 ;[012] <-- init ZL for CRC5 ldi ZH,hi8(CRC5_TABLE) ;[012] <-- init ZH for CRC5 in x2, USBIN ;[000] <-- bit 1 eor x1, x2 ;[001] bst x1, USBMINUS ;[002] bld shift, 1 ;[003] sbrc shift,1 ;[004] <-- skip next if PID1=0 (if a TOKEN) inc ZH ;[005] <-- init ZH for CRC16 ldi crchi,0xFE ;[006] <-- init crchi for CRC16 in r0, USBIN ;[007] <-- phase sbrc shift,1 ;[008] <-- skip next if PID1=0 (if a TOKEN) ldi ZL,0x54 ;[009] <-- init ZL for CRC16 ori x3, 0xff ;[010] [rx loop init] == ser x3, clear zero flag rjmp rxbit2 ;[011] ; ;[012] ;---------------------------------------------------------------------------- ; Receiver loop (numbers in brackets are cycles within byte after instr) ;---------------------------------------------------------------------------- /* byte oriented operations done during loop: bit 0: store data bit 1: SE0 check bit 2: overflow check bit 3: catch up bit 4: rjmp to achieve conditional jump range bit 5: CRC bit 6: catch up bit 7: jump, fixup bitstuff ; 87 [+ 2] cycles ------------------------------------------------------------------ */ ;-------------------------------------------------------------------------- unstuff5: ;[002] eor r0, x1 ;[003] sbrc r0, USBMINUS ;[004] nop2 ;[005] andi x3, ~0x20 ;[006] in r0, USBIN ;[007] <-- phase ori shift, 0x20 ;[008] mov x2, x1 ;[009] nop ;[010] nop2 ;[011] ; [---] ;[012] in x1, USBIN ;[000] <-- bit 6 eor r0, x1 ;[001] sbrc r0, USBMINUS ;[002] nop2 ;[003] eor x2, x1 ;[004] bst x2, USBMINUS ;[005] bld shift, 6 ;[006] no need to check bitstuffing, we just had one in r0, USBIN ;[007] <-- phase nop ;[008] rjmp didUnstuff5 ;[009] ; [---] ;[010] continueWithBit6: in x1, USBIN ;[000] <-- bit 6 breq unstuff5 ;[001] *** unstuff escape eor r0, x1 ;[002] eor x2, x1 ;[003] bst x2, USBMINUS ;[004] sbrc r0, USBMINUS ;[005] nop2 ;[006] didUnstuff6: ;[ ] in r0, USBIN ;[007] <-- phase bld shift, 6 ;[008] cpi shift, 0x02 ;[009] brlo unstuff6 ;[010] *** unstuff escape didUnstuff5: ;[ ] nop2 ;[011] ; [---] ;[012] in x2, USBIN ;[000] <-- bit 7 eor x1, x2 ;[001] bst x1, USBMINUS ;[002] bld shift, 7 ;[003] didUnstuff7: ;[ ] eor r0, x2 ;[004] sbrc r0, USBMINUS ;[005] nop2 ;[006] in r0, USBIN ;[007] <-- phase cpi shift, 0x04 ;[008] brsh rxLoop ;[009] ; [---] ;[010] ;------------------------------------------------------------------------- unstuff7: ;[ ] andi x3, ~0x80 ;[010] ori shift, 0x80 ;[011] nop ;[012] in x2, USBIN ;[000] <-- sample stuffed bit 7 nop ;[001] rjmp didUnstuff7 ;[002] ; [---] ;[003] ;------------------------------------------------------------------------------------ unstuff6: ;[011] andi x3, ~0x40 ;[012] in x1, USBIN ;[000] <-- bit 6 again set ;[001] ori shift, 0x40 ;[002] nop2 ;[003] ; [---] ;[004] rjmp didUnstuff6 ;[005] ; [---] ;[006] ;[071] unstuff0: ;[002] eor r0, x2 ;[003] sbrc r0,USBMINUS ;[004] nop2 ;[005] andi x2, USBMASK ;[006] check for SE0 in r0, USBIN ;[007] <-- phase breq didUnstuff0 ;[008] direct jump to se0 would be too long andi x3, ~0x01 ;[009] ori shift, 0x01 ;[010] mov x1, x2 ;[011] mov existing sample nop ;[012] in x2, USBIN ;[000] <-- bit 1 again rjmp didUnstuff0 ;[001] ; [---] ;[002] unstuff1: ;[002] eor r0, x1 ;[003] sbrc r0,USBMINUS ;[004] nop2 ;[005] andi x3, ~0x02 ;[006] in r0, USBIN ;[007] <-- phase ori shift, 0x02 ;[008] mov x2, x1 ;[009] nop ;[010] rjmp didUnstuff1 ;[011] ; [---] ;[012] unstuff2: ;[002] eor r0, x2 ;[003] sbrc r0,USBMINUS ;[004] nop2 ;[005] andi x3, ~0x04 ;[006] in r0, USBIN ;[007] <-- phase ori shift, 0x04 ;[008] mov x1, x2 ;[009] nop ;[010] rjmp didUnstuff2 ;[011] ; [---] ;[012] unstuff3: ;[011] nop ;[012] in x2, USBIN ;[000] <-- bit 3 again eor r0, x2 ;[001] sbrc r0,USBMINUS ;[002] nop2 ;[003] andi x3, ~0x08 ;[004] ori shift, 0x08 ;[005] nop ;[006] in r0, USBIN ;[007] <-- phase rjmp didUnstuff3 ;[008] ; [---] ;[009] rxLoop: eor x3, shift ;[011] reconstruct: x3 is 0 at bit locations we changed, 1 at others nop ;[012] ; in x1, USBIN ;[000] <-- bit 0 st y+, x3 ;[001] ; [---] ;[002] eor r0, x1 ;[003] sbrc r0,USBMINUS ;[004] nop2 ;[005] eor x2, x1 ;[006] in r0, USBIN ;[007] <-- phase mov ZL,x3 ;[008] <--CRC!!! bst x2, USBMINUS ;[009] bld shift, 0 ;[010] andi shift, 0xf9 ;[011] ser x3 ;[012] in x2, USBIN ;[000] <-- bit 1 breq unstuff0 ;[001] *** unstuff escape andi x2, USBMASK ;[002] SE0 check for bit 1 didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff breq se0 ;[003] eor r0, x2 ;[004] sbrc r0,USBMINUS ;[005] nop2 ;[006] in r0, USBIN ;[007] <-- phase eor x1, x2 ;[008] bst x1, USBMINUS ;[009] bld shift, 1 ;[010] andi shift, 0xf3 ;[011] eor ZL,crclo ;[012] <--CRC!!! didUnstuff1: ;[ ] rxbit2: in x1, USBIN ;[000] <-- bit 2 breq unstuff1 ;[001] *** unstuff escape eor r0, x1 ;[002] sbrc r0,USBMINUS ;[003] nop2 ;[004] subi cnt, 1 ;[005] overflow check brcs overflow ;[006] in r0, USBIN ;[007] <-- phase eor x2, x1 ;[008] bst x2, USBMINUS ;[009] bld shift, 2 ;[010] andi shift, 0xe7 ;[011] nop2 ;[012] ; nop ;[013] didUnstuff2: ;[ ] in x2, USBIN ;[000] <-- bit 3 breq unstuff2 ;[001] *** unstuff escape eor r0, x2 ;[002] sbrc r0,USBMINUS ;[003] nop2 ;[004] eor x1, x2 ;[005] bst x1, USBMINUS ;[006] in r0, USBIN ;[007] <-- phase bld shift, 3 ;[008] andi shift, 0xcf ;[009] didUnstuff3: ;[ ] breq unstuff3 ;[010] *** unstuff escape #if CRC5_TABLE & 0x100 sbrc ZH,1 ;[011] <--CRC!!! Check, if CRC5 #else sbrs ZH,1 ;[011] #endif clr crchi ;[012] <--CRC!!! clear crchi in x1, USBIN ;[000] <-- bit 4 eor x2, x1 ;[001] bst x2, USBMINUS ;[002] bld shift, 4 ;[003] didUnstuff4: ;[ ] eor r0, x1 ;[004] sbrc r0,USBMINUS ;[005] nop2 ;[006] in r0, USBIN ;[007] <-- phase andi shift, 0x9f ;[008] breq unstuff4 ;[009] *** unstuff escape lpm crclo,Z ;[010] <--CRC!!! ; [---] ;[011] <--CRC!!! ; [---] ;[012] <--CRC!!! in x2, USBIN ;[000] <-- bit 5 inc ZH ;[001] <--CRC!!! eor crclo,crchi ;[002] <--CRC!!! lpm crchi,Z ;[003] <--CRC!!! ; [---] ;[004] <--CRC!!! ; [---] ;[005] <--CRC!!! dec ZH ;[006] <--CRC!!! in r0, USBIN ;[007] <-- phase eor x1, x2 ;[008] bst x1, USBMINUS ;[009] bld shift, 5 ;[010] andi shift, 0x3f ;[011] rjmp continueWithBit6;[012] ; [---] ;[013] overflow: rjmp ignorePacket ;------------------------------------------------------------------- unstuff4: ;[010] andi x3, ~0x10 ;[011] nop ;[012] in x1, USBIN ;[000] <-- bit 4 again ori shift, 0x10 ;[001] rjmp didUnstuff4 ;[002] ; [---] ;[003] ;---------------------------------------------------------------------------- ; Processing of received packet (numbers in brackets are cycles after center of SE0) ;---------------------------------------------------------------------------- ;This is the only non-error exit point for the software receiver loop ;we don't check any CRCs here because there is no time left. #define token x1 se0: eor ZL,crclo ;[5] <--CRC!!! lpm crclo,Z ;[6] <--CRC!!! ; [---] ;[7] <--CRC!!! ; [---] ;[8] <--CRC!!! subi cnt, USB_BUFSIZE ;[9] neg cnt ;[10] cpi cnt, 3 ;[11] ldi x2, 1<<0><1><0><1><0><1><0><0> ; ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ;-6 0 13 13 14 13 13 14 13 13 14 ; 0 13 26 40 53 66 80 ;12MHz ;<0><1><0><1><0><1><0><0> ; ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ; 0 8 16 24 32 40 56 ; handleSetupOrOut: ;[39] #if USB_CFG_IMPLEMENT_FN_WRITEOUT /* if we have data for second OUT endpoint, set usbCurrentTok to -1 */ sbrc x3, 7 ;[40] skip if endpoint 0 ldi token, -1 ;[41] indicate that this is endpoint 1 OUT #endif sts usbCurrentTok, token ;[42] ; [---] ;[43] pop ZL ;[44] ; [---] ;[45] pop x5 ;[46] ; [---] ;[47] pop cnt ;[48] ; [---] ;[49] pop x4 ;[50] ; [---] ;[51] pop ZH ;[52] ; [---] ;[53] pop shift ;[54] ; [---] ;[55] pop x3 ;[56] ; [---] ;[57] pop x2 ;[58] ; [---] ;[59] pop x1 ;[60] ; [---] ;[61] pop YH ;[62] ; [---] ;[63] pop YL ;[64] ; [---] ;[65] in r0, USB_INTR_PENDING ;[66] sbrc r0, USB_INTR_PENDING_BIT;[67] check whether data is already arriving rjmp waitForJ ;[68] save the pops and pushes -- a new interrupt is aready pending rjmp sofError ;[69] not an error, but it does the pops and reti we want handleData: ;[23] inc ZH ;[24] <--CRC!!! eor crclo,crchi ;[25] <--CRC!!! lpm crchi,Z ;[26] <--CRC!!! ; [---] ;[27] <--CRC!!! ; [---] ;[28] <--CRC!!! subi crclo,0x01 ;[29] sbci crchi,0xB0 ;[30] brne loc_doReturn ;[31] lds token, usbCurrentTok ;[32] ; [---] ;[33] tst token ;[34] breq loc_doReturn ;[35] lds x2, usbRxLen ;[36] ; [---] ;[37] tst x2 ;[38] brne sendNakAndReti ;[39] ; 2006-03-11: The following two lines fix a problem where the device was not ; recognized if usbPoll() was called less frequently than once every 4 ms. cpi cnt, 4 ;[40] zero sized data packets are status phase only -- ignore and ack brmi sendAckAndReti ;[41] keep rx buffer clean -- we must not NAK next SETUP sts usbRxLen, cnt ;[42] store received data, swap buffers ; [---] ;[43] sts usbRxToken, token ;[44] ; [---] ;[45] lds x2, usbInputBufOffset ;[46] swap buffers ; [---] ;[47] ldi cnt, USB_BUFSIZE ;[48] sub cnt, x2 ;[49] sts usbInputBufOffset, cnt ;[50] buffers now swapped ; [---] ;[51] rjmp sendAckAndReti ;[52] 54 + 17 = 71 until SOP handleIn: ;[35] ;We don't send any data as long as the C code has not processed the current ;input data and potentially updated the output data. That's more efficient ;in terms of code size than clearing the tx buffers when a packet is received. lds x1, usbRxLen ;[36] ; [---] ;[37] cpi x1, 1 ;[38] negative values are flow control, 0 means "buffer free" brge sendNakAndReti ;[39] unprocessed input packet? ldi x1, USBPID_NAK ;[40] prepare value for usbTxLen #if USB_CFG_HAVE_INTRIN_ENDPOINT sbrc x3, 7 ;[41] x3 contains addr + endpoint rjmp handleIn1 ;[42] #endif lds cnt, usbTxLen ;[43] ; [---] ;[44] sbrc cnt, 4 ;[45] all handshake tokens have bit 4 set rjmp sendCntAndReti ;[46] 48 + 16 = 64 until SOP sts usbTxLen, x1 ;[47] x1 == USBPID_NAK from above ; [---] ;[48] ldi YL, lo8(usbTxBuf) ;[49] ldi YH, hi8(usbTxBuf) ;[50] rjmp usbSendAndReti ;[51] 53 + 12 = 65 until SOP ; Comment about when to set usbTxLen to USBPID_NAK: ; We should set it back when we receive the ACK from the host. This would ; be simple to implement: One static variable which stores whether the last ; tx was for endpoint 0 or 1 and a compare in the receiver to distinguish the ; ACK. However, we set it back immediately when we send the package, ; assuming that no error occurs and the host sends an ACK. We save one byte ; RAM this way and avoid potential problems with endless retries. The rest of ; the driver assumes error-free transfers anyway. #if USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */ handleIn1: ;[43] #if USB_CFG_HAVE_INTRIN_ENDPOINT3 ; 2006-06-10 as suggested by O.Tamura: support second INTR IN / BULK IN endpoint ldd x2, y+2 ;[44] ; [---] ;[45] sbrc x2, 0 ;[46] rjmp handleIn3 ;[47] #endif lds cnt, usbTxLen1 ;[48] ; [---] ;[49] sbrc cnt, 4 ;[50] all handshake tokens have bit 4 set rjmp sendCntAndReti ;[51] 53 + 16 = 69 until SOP sts usbTxLen1, x1 ;[52] x1 == USBPID_NAK from above ; [---] ;[53] ldi YL, lo8(usbTxBuf1) ;[54] ldi YH, hi8(usbTxBuf1) ;[55] rjmp usbSendAndReti ;[56] 58 + 12 = 70 until SOP #endif ; USB spec says: ; idle = J ; J = (D+ = 0), (D- = 1) ; K = (D+ = 1), (D- = 0) ; Spec allows 7.5 bit times from EOP to SOP for replies bitstuff7: eor x1, x4 ;[4] ldi x2, 0 ;[5] nop2 ;[6] C is zero (brcc) rjmp didStuff7 ;[8] bitstuffN: eor x1, x4 ;[5] ldi x2, 0 ;[6] lpm ;[7] 3 cycle NOP, modifies r0 out USBOUT, x1 ;[10] <-- out SUBI ZL,0x55 ;+2.33 cycles brcs $+2 rjmp didStuffN ;[0] #define bitStatus x3 sendNakAndReti: ldi cnt, USBPID_NAK ;[-19] rjmp sendCntAndReti ;[-18] sendAckAndReti: ldi cnt, USBPID_ACK ;[-17] sendCntAndReti: mov r0, cnt ;[-16] ldi YL, 0 ;[-15] R0 address is 0 ldi YH, 0 ;[-14] ldi cnt, 2 ;[-13] ; rjmp usbSendAndReti fallthrough ;usbSend: ;pointer to data in 'Y' ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] ;uses: x1...x4, shift, cnt, Y ;Numbers in brackets are time since first bit of sync pattern is sent usbSendAndReti: ; 12 cycles until SOP in x2, USBDDR ;[-12] ori x2, USBMASK ;[-11] sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) in x1, USBOUT ;[-8] port mirror for tx loop out USBDDR, x2 ;[-7] <- acquire bus ; need not init x2 (bitstuff history) because sync starts with 0 ldi x4, USBMASK ;[-6] exor mask ldi shift, 0x80 ;[-5] sync byte is first byte sent ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes byteloop: bitloop: sbrs shift, 0 ;[8] [-3] eor x1, x4 ;[9] [-2] out USBOUT, x1 ;[10] [-1] <-- out SUBI ZL,0x55 ;+2.33 cycles brcs $+2 ror shift ;[0] ror x2 ;[1] didStuffN: cpi x2, 0xfc ;[2] brcc bitstuffN ;[3] nop ;[4] subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value sbrs shift, 0 ;[7] eor x1, x4 ;[8] ror shift ;[9] didStuff7: out USBOUT, x1 ;[10] <-- out SUBI ZL,0x55 ;+2.33 cycles brcs $+2 ror x2 ;[0] cpi x2, 0xfc ;[1] brcc bitstuff7 ;[2] ld shift, y+ ;[3] dec cnt ;[5] brne byteloop ;[6] ;make SE0: cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 25 to 30 cycles] lds x2, usbNewDeviceAddr;[8] out USBOUT, x1 ;[10] <-- out SE0 -- from now 2 bits = 27 cycles until bus idle ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: ;set address only after data packet was sent, not after handshake subi YL, 2 ;[0] sbci YH, 0 ;[1] breq skipAddrAssign ;[2] sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer skipAddrAssign: ;end of usbDeviceAddress transfer ldi x2, 1<>1)^CRC16polyInv) ;shift the CRC and make xor with polynom else ;if not CRC16 var (CRC16>>1) ;only shift the CRC endif Message var (Message>>1) ;go to next bit in Message endr endm v var 0 REPT 256 CRC16 var 0 COMPUTE_CRC16 v ASEGN ABSOLUTE:CODE:ROOT,CRC16_TABLE+0x0000+v DB CRC16 ASEGN ABSOLUTE:CODE:ROOT,CRC16_TABLE+0x0100+v DB CRC16>>8 v var v+1 ENDR //CRC5_TABLE EQU 0x1D00 CRC5init var 0 CRC5polyInv var 0x14 ;CRC5 polynom (00101) in inverted bits order COMPUTE_CRC5 MACRO byte Message var byte rept 8 if ((CRC5^Message)&1) ;if xor of CRC5[MSB] and Message[LSB] is one (note: CRC5 has now inverted bit order) CRC5 var ((CRC5>>1)^CRC5polyInv) ;shift the CRC and make xor with polynom else ;if not CRC5 var (CRC5>>1) ;only shift the CRC endif Message var (Message>>1) ;go to next bit in Message endr endm v var 0 REPT 256 CRC5 var 0 COMPUTE_CRC5 v ASEGN ABSOLUTE:CODE:ROOT,CRC5_TABLE+v DB CRC5 v var v+1 ENDR #endif end