📄 usbdrvasm.s
字号:
isData:
lds x2, usbCurrentTok ;2
tst x2 ;1
breq rxDoReturn ;1 for other device or spontaneous data -- ignore
lds x1, usbRxLen ;2
cpi x1, 0 ;1
brne sendNakAndReti ;1 no buffer space available / {30, 38} from SE0 end
; 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 ;1 zero sized data packets are status phase only -- ignore and ack
brmi sendAckAndReti ;1 keep rx buffer clean -- we must not NAK next SETUP
sts usbRxLen, cnt ;2 store received data, swap buffers
sts usbRxToken, x2 ;2
lds x1, usbAppBuf ;2
sts usbAppBuf, YL ;2
sts usbInputBuf, x1 ;2 buffers now swapped
rjmp sendAckAndReti ;2 -> {43, 51} from SE0 end
handleIn: ; {18, 26} from SE0 end
cp x2, shift ;1 shift contains our device addr
brne rxDoReturn ;1 other device
#if USB_CFG_HAVE_INTRIN_ENDPOINT
sbrc x3, 7 ;2 x3 contains addr + endpoint
rjmp handleIn1 ;0
#endif
lds cnt, usbTxLen ;2
cpi cnt, -1 ;1
breq sendNakAndReti ;1 -> {27, 35} from SE0 end
ldi x1, -1 ;1
sts usbTxLen, x1 ;2 buffer is now free
ldi YL, lo8(usbTxBuf) ;1
ldi YH, hi8(usbTxBuf) ;1
rjmp usbSendAndReti ;2 -> {34, 43} from SE0 end
; Comment about when to set usbTxLen to -1:
; We should set it back to -1 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 to -1 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.
otherOutOrSetup:
clr x1
sts usbCurrentTok, x1
rxDoReturn:
pop x3 ;2
pop YL ;2
pop YH ;2
rjmp sofError ;2
isSetupOrOut: ; we must be fast here -- a data package may follow / {,24} into next frame
cp x2, shift ;1 shift contains our device addr
brne otherOutOrSetup ;1 other device -- ignore
#if USB_CFG_IMPLEMENT_FN_WRITEOUT /* if we need second OUT endpoint, store endpoint address */
andi x1, 0x7f ;1 mask out MSb in token
andi x3, 0x80 ;1 mask out all but endpoint address
or x1, x3 ;1 merge endpoint into currentToken
sts usbCurrentTok, x1 ;2
brmi dontResetEP0 ;1 endpoint 1 -> don't reset endpoint 0 input
#else
sts usbCurrentTok, x1 ;2
#endif
;A transmission can still have data in the output buffer while we receive a
;SETUP package with an IN phase. To avoid that the old data is sent as a reply,
;we abort transmission.
ldi x1, -1 ;1
sts usbMsgLen, x1 ;2
sts usbTxLen, x1 ;2 abort transmission
dontResetEP0:
pop x3 ;2
pop YL ;2
in x1, USB_INTR_PENDING;1
sbrc x1, USB_INTR_PENDING_BIT;1 check whether data is already arriving {,41} into next frame
rjmp shortcutToStart ;2 save the pops and pushes -- a new interrupt is aready pending
;If the jump above was not taken, we can be at {,2} into the next frame here
pop YH ;2
txDoReturn:
sofError: ; error in start of frame -- ignore frame
ldi x1, 1<<USB_INTR_PENDING_BIT;1 many int0 events occurred during our processing -- clear pending flag
out USB_INTR_PENDING, x1;1
pop shift ;2
pop cnt ;2
pop x2 ;2
pop x1 ;2
out SREG, x1 ;1
pop x1 ;2
reti ;4 -> {,21} into next frame -> up to 3 sync bits missed
sendNakAndReti: ; 21 cycles until SOP
ldi YL, lo8(usbNakBuf) ;1
ldi YH, hi8(usbNakBuf) ;1
rjmp usbSendToken ;2
sendAckAndReti: ; 19 cycles until SOP
ldi YL, lo8(usbAckBuf) ;1
ldi YH, hi8(usbAckBuf) ;1
usbSendToken:
ldi cnt, 2 ;1
;;;;rjmp usbSendAndReti fallthrough
; USB spec says:
; idle = J
; J = (D+ = 0), (D- = 1) or USBOUT = 0x01
; K = (D+ = 1), (D- = 0) or USBOUT = 0x02
; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles)
;usbSend:
;pointer to data in 'Y'
;number of bytes in 'cnt' -- including sync byte
;uses: x1...x4, shift, cnt, Y
usbSendAndReti: ; SOP starts 16 cycles after call
push x4 ;2
in x1, USBOUT ;1
cbr x1, USBMASK ;1 mask out data bits
ori x1, USBIDLE ;1 idle
out USBOUT, x1 ;1 prepare idle state
ldi x4, USBMASK ;1 exor mask
in x2, USBDDR ;1
ori x2, USBMASK ;1 set both pins to output
out USBDDR, x2 ;1 <-- acquire bus now
; need not init x2 (bitstuff history) because sync starts with 0
ldi shift, 0x80 ;1 sync byte is first byte sent
rjmp txLoop ;2 -> 13 + 3 = 16 cycles until SOP
#if USB_CFG_HAVE_INTRIN_ENDPOINT /* placed here due to relative jump range */
handleIn1: ;{23, 31} from SE0
ldi x1, -1 ;1
#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 ;2
sbrc x2, 0 ;2 1
rjmp handleIn3 ;0 2
#endif
lds cnt, usbTxLen1 ;2
cpi cnt, -1 ;1
breq sendNakAndReti ;1
sts usbTxLen1, x1 ;2
ldi YL, lo8(usbTxBuf1);1
ldi YH, hi8(usbTxBuf1);1
rjmp usbSendAndReti ;2 -> arrives at usbSendAndReti {34, 42} from SE0
#if USB_CFG_HAVE_INTRIN_ENDPOINT3
handleIn3:
lds cnt, usbTxLen3 ;2
cpi cnt, -1 ;1
breq sendNakAndReti ;1
sts usbTxLen3, x1 ;2
ldi YL, lo8(usbTxBuf3);1
ldi YH, hi8(usbTxBuf3);1
rjmp usbSendAndReti ;2 -> arrives at usbSendAndReti {39, 47} from SE0
#endif
#endif
bitstuff0: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
out USBOUT, x1 ;1 <-- out
rjmp didStuff0 ;2 branch back 2 cycles earlier
bitstuff1: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
sec ;1 set carry so that brsh will not jump
out USBOUT, x1 ;1 <-- out
rjmp didStuff1 ;2 jump back 1 cycle earler
bitstuff2: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
rjmp didStuff2 ;2 jump back 3 cycles earlier and do out
bitstuff3: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
rjmp didStuff3 ;2 jump back earlier
txLoop:
sbrs shift, 0 ;1
eor x1, x4 ;1
out USBOUT, x1 ;1 <-- out
ror shift ;1
ror x2 ;1
didStuff0:
cpi x2, 0xfc ;1
brsh bitstuff0 ;1
sbrs shift, 0 ;1
eor x1, x4 ;1
ror shift ;1
out USBOUT, x1 ;1 <-- out
ror x2 ;1
cpi x2, 0xfc ;1
didStuff1:
brsh bitstuff1 ;1
sbrs shift, 0 ;1
eor x1, x4 ;1
ror shift ;1
ror x2 ;1
didStuff2:
out USBOUT, x1 ;1 <-- out
cpi x2, 0xfc ;1
brsh bitstuff2 ;1
sbrs shift, 0 ;1
eor x1, x4 ;1
ror shift ;1
ror x2 ;1
didStuff3:
cpi x2, 0xfc ;1
out USBOUT, x1 ;1 <-- out
brsh bitstuff3 ;1
nop2 ;2
ld x3, y+ ;2
sbrs shift, 0 ;1
eor x1, x4 ;1
out USBOUT, x1 ;1 <-- out
ror shift ;1
ror x2 ;1
didStuff4:
cpi x2, 0xfc ;1
brsh bitstuff4 ;1
sbrs shift, 0 ;1
eor x1, x4 ;1
ror shift ;1
out USBOUT, x1 ;1 <-- out
ror x2 ;1
cpi x2, 0xfc ;1
didStuff5:
brsh bitstuff5 ;1
sbrs shift, 0 ;1
eor x1, x4 ;1
ror shift ;1
ror x2 ;1
didStuff6:
out USBOUT, x1 ;1 <-- out
cpi x2, 0xfc ;1
brsh bitstuff6 ;1
sbrs shift, 0 ;1
eor x1, x4 ;1
ror shift ;1
ror x2 ;1
didStuff7:
cpi x2, 0xfc ;1
out USBOUT, x1 ;1 <-- out
brsh bitstuff7 ;1
mov shift, x3 ;1
dec cnt ;1
brne txLoop ;2 | 1
cbr x1, USBMASK ;1 prepare SE0 [spec says EOP may be 15 to 18 cycles]
pop x4 ;2
out USBOUT, x1 ;1 <-- out SE0 -- from now 2 bits = 16 cycles until bus idle
ldi cnt, 2 ;| takes cnt * 3 cycles
se0Delay: ;|
dec cnt ;|
brne se0Delay ;| -> 2 * 3 = 6 cycles
;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm:
lds x2, usbNewDeviceAddr ;2
subi YL, lo8(usbNakBuf + 2) ;1
sbci YH, hi8(usbNakBuf + 2) ;1
breq skipAddrAssign ;2
sts usbDeviceAddr, x2 ;0 if not skipped: SE0 is one cycle longer
skipAddrAssign:
;end of usbDeviceAddress transfer
ori x1, USBIDLE ;1
in x2, USBDDR ;1
cbr x2, USBMASK ;1 set both pins to input
out USBOUT, x1 ;1 <-- out J (idle) -- end of SE0 (EOP signal)
cbr x1, USBMASK ;1 configure no pullup on both pins
pop x3 ;2
pop YL ;2
out USBDDR, x2 ;1 <-- release bus now
out USBOUT, x1 ;1 set pullup state
pop YH ;2
rjmp txDoReturn ;2 [we want to jump to rxDoReturn, but this saves cycles]
bitstuff4: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
out USBOUT, x1 ;1 <-- out
rjmp didStuff4 ;2 jump back 2 cycles earlier
bitstuff5: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
sec ;1 set carry so that brsh is not taken
out USBOUT, x1 ;1 <-- out
rjmp didStuff5 ;2 jump back 1 cycle earlier
bitstuff6: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
rjmp didStuff6 ;2 jump back 3 cycles earlier and do out there
bitstuff7: ;1 (for branch taken)
eor x1, x4 ;1
ldi x2, 0 ;1
rjmp didStuff7 ;2 jump back 4 cycles earlier
; ######################## 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 byte r22
# define bitCnt r19
# define polyL r20
# define polyH r21
# define scratch r23
#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
#endif
; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -