; USB18.ASM

; Put Address into source pointer
mSetSourcePointer macro Address
	movlw	low (Address)
	movwf	pSrc
	movlw	high (Address)
	movwf	pSrc + 1
	endm

; Put Address into destination pointer
mSetDestinationPointer macro Address
	movlw	low (Address)
	movwf	pDst
	movlw	high (Address)
	movwf	pDst + 1
	endm

; Get count from first location of ROM table pointed to by pSrc
mGetRomTableCount macro
	movff	pSrc, TBLPTRL		; Set source address
	movff	pSrc + 1, TBLPTRH
	clrf	TBLPTRU
        tblrd   *			; Read count
	movff	TABLAT, wCount
	clrf	wCount + 1
	endm


; From usb9.c  line 70
;/******************************************************************************
; * Function:        void USBCheckStdRequest(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine checks the setup data packet to see if it
; *                  knows how to handle it
; *
; * Note:            None
; *****************************************************************************/
USBCheckStdRequest
	movlb	high 0x400		; Point to proper bank
	movf	SetupPkt, W		; RequestType = STANDARD?
	andlw	0x60			; Mask to proper bits
	sublw	(STANDARD) << 5
	bnz	USBCheckStdRequestExit	; No
	movlw	SET_ADR			; Handle request
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest1
	movlw	ADR_PENDING_STATE
	movwf	usb_device_state
	bra	USBStdSetSessionOwnerUSB9

; GET_DESCRIPTOR request?
USBCheckStdRequest1
	movlw	GET_DSC
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest2
	bra	USBStdGetDscHandler

; GET_CONFIGURATION request?
USBCheckStdRequest2
	movlw	GET_CFG
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest3
	mSetSourcePointer usb_active_cfg
	movlw	1
	movwf	wCount
	clrf	wCount + 1
	bcf	usb_stat, ctrl_trf_mem	; Indicate RAM
	bra	USBStdSetSessionOwnerUSB9

; SET_CONFIGURATION request?
USBCheckStdRequest3
	movlw	SET_CFG
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest4
	bra	USBStdSetCfgHandler

; GET_STATUS request?
USBCheckStdRequest4
	movlw	GET_STATUS
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest5
	bra	USBStdGetStatusHandler

; CLEAR_FEATURE request?
USBCheckStdRequest5
	movlw	CLR_FEATURE
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest6
	bra	USBStdFeatureReqHandler

; SET_FEATURE request?
USBCheckStdRequest6
	movlw	SET_FEATURE
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest7
	bra	USBStdFeatureReqHandler

; GET_INTERFACE request?
USBCheckStdRequest7
	movlw	GET_INTF
	cpfseq	SetupPkt + bRequest
	bra	USBCheckStdRequest8
	mSetSourcePointer usb_alt_intf
	movf	SetupPkt + bIntfID, W
	addwf	pSrc, F
	movlw	1
	movwf	wCount
	clrf	wCount + 1
	bcf	usb_stat, ctrl_trf_mem	; Indicate RAM
	bra	USBStdSetSessionOwnerUSB9

; SET_INTERFACE request?
USBCheckStdRequest8
	movlw	SET_INTF
	cpfseq	SetupPkt + bRequest
	return
	lfsr	2, usb_alt_intf
	movf	SetupPkt + bIntfID, W
	movff	SetupPkt + bAltID, PLUSW2
; Branch here after decoding one of the above USB standard requests.
; Assign a value to ctrl_trf_session_owner, to prevent stalling
USBStdSetSessionOwnerUSB9
	movlw	MUID_USB9
	movwf	ctrl_trf_session_owner
USBCheckStdRequestExit
	return

; From usb9.c  line 136
;/******************************************************************************
; * Function:        void USBStdGetDscHandler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine handles the standard GET_DESCRIPTOR request.
; *                  It utilizes tables dynamically looks up descriptor size.
; *                  This routine should never have to be modified if the tables
; *                  in usbdsc.c are declared correctly.
; *
; * Note:            None
; *****************************************************************************/
USBStdGetDscHandler
	movlb	high 0x400		; Point to proper bank
	movlw	0x80
	cpfseq	SetupPkt + bmRequestType
	return
	movlw	DSC_DEV
	cpfseq	SetupPkt + bDscType
	bra	USBStdGetDscHandler1
	mSetSourcePointer DeviceDescriptor
	mGetRomTableCount		; Set wCount
	bsf	usb_stat, ctrl_trf_mem	; Indicate ROM
	bra	USBStdSetSessionOwnerUSB9
USBStdGetDscHandler1
	movlw	DSC_CFG
	cpfseq	SetupPkt + bDscType
	bra	USBStdGetDscHandler2
	mSetSourcePointer Config1
	movlw	low Config1Len		; Set wCount to total length
	movwf	TBLPTRL
	movlw	high Config1Len
	movwf	TBLPTRH
	clrf	TBLPTRU
	tblrd	*+			; Read count low
	movff	TABLAT, wCount
	tblrd	*+			; Ignore RETLW opcode
	tblrd	*+			; Read count high
	movff	TABLAT, wCount + 1
	bsf	usb_stat, ctrl_trf_mem	; Indicate ROM
	bra	USBStdSetSessionOwnerUSB9
USBStdGetDscHandler2
	movlw	DSC_STR
	cpfseq	SetupPkt + bDscType
	return
	clrf	TBLPTRU
	clrf	TBLPTRH
	rlncf	SetupPkt + bDscIndex, W	; Index * 2
	addlw	low (USB_SD_Ptr)	; Add element offset to low address
	movwf	TBLPTRL
	movlw	high (USB_SD_Ptr)
	addwfc	TBLPTRH, F
        tblrd   *+			; Get the data to TABLAT and move pointer forward
	movff	TABLAT, pSrc		; Get low source address
        tblrd   *
	movff	TABLAT, pSrc + 1	; Get high source address
	mGetRomTableCount		; Set wCount
	bsf	usb_stat, ctrl_trf_mem	; Indicate ROM
	bra	USBStdSetSessionOwnerUSB9


; From usb9.c  line 180
;/******************************************************************************
; * Function:        void USBStdSetCfgHandler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine first disables all endpoints by clearing
; *                  UEP registers. It then configures (initializes) endpoints
; *                  specified in the modifiable section.
; *
; * Note:            None
; *****************************************************************************/
USBStdSetCfgHandler
	movlb	high 0x400		; Point to proper bank
	movlw	MUID_USB9
	movwf	ctrl_trf_session_owner
	lfsr	2, UEP1			; Reset all non-EP0 UEPn registers
	movlw	15
USBStdSetCfgHandlerClearEPLoop
	clrf	POSTINC2
	decfsz	WREG, F
	bra	USBStdSetCfgHandlerClearEPLoop
	lfsr	2, usb_alt_intf		; Clear usb_alt_intf array
	movlw	MAX_NUM_INT
USBStdSetCfgHandlerClearAltLoop
	clrf	POSTINC2
	decfsz	WREG, F
	bra	USBStdSetCfgHandlerClearAltLoop
	movf	SetupPkt + bCfgValue, W
	movwf	usb_active_cfg
	bnz	USBStdSetCfgHandler1
	movlw	ADDRESS_STATE		; SetupPkt + bCfgValue = 0
	movwf	usb_device_state
	return
USBStdSetCfgHandler1
	movlw	CONFIGURED_STATE
	movwf	usb_device_state
#ifdef USB_USE_HID
	rcall	HIDInitEP
#endif
#ifdef USB_USE_CDC
	rcall	CDCInitEP
#endif
	return


; From usb9.c  line 224
;/******************************************************************************
; * Function:        void USBStdGetStatusHandler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine handles the standard GET_STATUS request
; *
; * Note:            None
; *****************************************************************************/
USBStdGetStatusHandler
	movlb	high 0x400		; Point to proper bank
	clrf	CtrlTrfData		; Initialize content
	clrf	CtrlTrfData + 1
	movf	SetupPkt, W		; Recipient = RCPT_DEV?
	andlw	0x1f			; Mask to lower 5 bits
	sublw	RCPT_DEV
	bnz	USBStdGetStatusHandler1	; No
;
; Recipient of this Setup packet was "Device": set bits to indicate power & remote wakeup
; Decoding of "Self-powered" & "Remote Wakeup"
; _byte0: bit0: Self-Powered Status [0] Bus-Powered [1] Self-Powered
;         bit1: RemoteWakeup        [0] Disabled    [1] Enabled
;
#ifdef USB_SELF_POWER
	bsf	CtrlTrfData, 0
#endif
	btfsc	usb_stat, RemoteWakeup
	bsf	CtrlTrfData, 1
	bra	USBStdGetStatusSetSessionOwner
;
USBStdGetStatusHandler1
	movf	SetupPkt, W		; Recipient = RCPT_INTF?
	andlw	0x1f			; Mask to lower 5 bits
	sublw	RCPT_INTF
	bnz	USBStdGetStatusHandler2	; No
;
; Recipient of this Setup packet was "Interface": No data to update
	bra	USBStdGetStatusSetSessionOwner
;
USBStdGetStatusHandler2
	movf	SetupPkt, W		; Recipient = RCPT_EP?
	andlw	0x1f			; Mask to lower 5 bits
	sublw	RCPT_EP
	bnz	USBStdGetStatusHandler3	; No
;
; Recipient of this Setup packet was "Endpoint"
	rcall	USBCalcEPAddress	; Put endpoint buffer address in FSR2
	movf	INDF2, W
	andlw	_BSTALL
	bz	USBStdGetStatusSetSessionOwner
	bsf	CtrlTrfData, 0
USBStdGetStatusSetSessionOwner
	movlw	MUID_USB9
	movwf	ctrl_trf_session_owner
USBStdGetStatusHandler3
	movlw	MUID_USB9
	cpfseq	ctrl_trf_session_owner
	return
	mSetSourcePointer CtrlTrfData
	movlw	2			; Set count
	movwf	wCount
	clrf	wCount + 1
	bcf	usb_stat, ctrl_trf_mem	; Indicate RAM
	return

; From usb9.c  line 281
;/******************************************************************************
; * Function:        void USBStdFeatureReqHandler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine handles the standard SET & CLEAR FEATURES
; *                  requests
; *
; * Note:            None
; *****************************************************************************/
USBStdFeatureReqHandler
	movlb	high 0x400		; Point to proper bank
	movlw	DEVICE_REMOTE_WAKEUP	; If Feature = DEVICE_REMOTE_WAKEUP &
	cpfseq	SetupPkt + bFeature
	bra	USBStdFeatureReqHandler1
	movf	SetupPkt, W	; Recipient = RCPT_DEV?
	andlw	0x1f			; Mask to lower 5 bits
	sublw	RCPT_DEV
	bnz	USBStdFeatureReqHandler1	; No
	bsf	usb_stat, RemoteWakeup	; Preset RemoteWakeup to 1
	movlw	SET_FEATURE		; Request = SET_FEATURE?
	cpfseq	SetupPkt + bRequest
	bcf	usb_stat, RemoteWakeup	; No, RemoteWakeup = 0
	bra	USBStdSetSessionOwnerUSB9
USBStdFeatureReqHandler1
	movlw	ENDPOINT_HALT		; If Feature = ENDPOINT_HALT &
	cpfseq	SetupPkt + bFeature
USBStdFeatureReqHandlerExit
	return
	movf	SetupPkt, W	; Recepient = RCPT_EP &
	andlw	0x1f			; Mask to lower 5 bits
	sublw	RCPT_EP
	bnz	USBStdFeatureReqHandlerExit
	movf	SetupPkt + bEPID, W	; EPNum != 0
	andlw	0x0f			; Mask to EPNum
	bz	USBStdFeatureReqHandlerExit
	rcall	USBCalcEPAddress	; Put endpoint buffer address in FSR2
	movlw	SET_FEATURE		; Request = SET_FEATURE?
	cpfseq	SetupPkt + bRequest
	bra	USBStdFeatureReqHandler2	; No
	movlw	_USIE|_BSTALL
	movwf	INDF2			; Put in endpoint buffer
	bra	USBStdSetSessionOwnerUSB9
USBStdFeatureReqHandler2
	movlw	_UCPU|_DAT1		; IN
	btfss	SetupPkt + bEPID, EPDir	; EPDir = 1 (IN)?
	movlw	_USIE|_DAT0|_DTSEN	; No - OUT
	movwf	INDF2			; Put in endpoint buffer
	bra	USBStdSetSessionOwnerUSB9


; Put endpoint buffer address in FSR2 (ep0Bo+(EPNum*8)+(EPDir*4))
USBCalcEPAddress
	lfsr	2, ep0Bo		; Point FSR2 to beginning of buffer area
	rlncf	SetupPkt + bEPID, W	; Move endpoint direction to C
	rlcf	SetupPkt + bEPID, W	; Endpoint number * 8 (roll in ep direction)
	rlncf	WREG, F
	rlncf	WREG, F
	addwf	FSR2L, F		; Add to FSR2 (can't overflow to FSR2H)
	return



; From usbctrltrf.c line 78
;/******************************************************************************
; * Function:        void USBCtrlEPService(void)
; *
; * PreCondition:    USTAT is loaded with a valid endpoint address.
; *
; * Input:           None
; *
; * Output:          trnifcleared - 0xff for TRNIF cleared, - for not
; *
; * Side Effects:    None
; *
; * Overview:        USBCtrlEPService checks for three transaction types that
; *                  it knows how to service and services them:
; *                  1. EP0 SETUP
; *                  2. EP0 OUT
; *                  3. EP0 IN
; *                  It ignores all other types (i.e. EP1, EP2, etc.)
; *
; * Note:            None
; *****************************************************************************/
USBCtrlEPService
	movlb	high 0x400		; Point to proper bank
	clrf	trnifcleared		; 1.2 Indicate TRNIF not cleared
	movlw	EP00_OUT
	cpfseq	USTAT
	bra	USBCtrlEPService1
	bcf	UIR, TRNIF		; 1.2
	setf	trnifcleared		; 1.2 Indicate TRNIF cleared
	movf	ep0Bo + Stat, W
	andlw	0x3c			; Mask to PID
	sublw	(SETUP_TOKEN) << 2
	bz	USBCtrlTrfSetupHandler
	bra	USBCtrlTrfOutHandler
USBCtrlEPService1
	movlw	EP00_IN
	cpfseq	USTAT
	return
	bcf	UIR, TRNIF		; 1.2
	setf	trnifcleared		; 1.2 Indicate TRNIF cleared
	bra	USBCtrlTrfInHandler


; From usbctrltrf.c line 133
;/******************************************************************************
; * Function:        void USBCtrlTrfSetupHandler(void)
; *
; * PreCondition:    SetupPkt buffer is loaded with valid USB Setup Data
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine is a task dispatcher and has 3 stages.
; *                  1. It initializes the control transfer state machine.
; *                  2. It calls on each of the module that may know how to
; *                     service the Setup Request from the host.
; *                     Module Example: USB9, HID, CDC, MSD, ...
; *                     As new classes are added, ClassReqHandler table in
; *                     usbdsc.c should be updated to call all available
; *                     class handlers.
; *                  3. Once each of the modules has had a chance to check if
; *                     it is responsible for servicing the request, stage 3
; *                     then checks direction of the transfer to determine how
; *                     to prepare EP0 for the control transfer.
; *                     Refer to USBCtrlEPServiceComplete() for more details.
; *
; * Note:            Microchip USB Firmware has three different states for
; *                  the control transfer state machine:
; *                  1. WAIT_SETUP
; *                  2. CTRL_TRF_TX
; *                  3. CTRL_TRF_RX
; *                  Refer to firmware manual to find out how one state
; *                  is transitioned to another.
; *
; *                  A Control Transfer is composed of many USB transactions.
; *                  When transferring data over multiple transactions,
; *                  it is important to keep track of data source, data
; *                  destination, and data count. These three parameters are
; *                  stored in pSrc,pDst, and wCount. A flag is used to
; *                  note if the data source is from ROM or RAM.
; *
; *****************************************************************************/
USBCtrlTrfSetupHandler
	movlb	high 0x400		; Point to proper bank
	movlw	_UCPU			; 1.2
	btfsc	ep0Bi + Stat, UOWN	; 1.2
	movwf	ep0Bi + Stat		; 1.2 Compensate for after a STALL
	movlw	SHORT_PKT_NOT_USED	; 1.2
	movwf	short_pkt_status	; 1.2
	movlw	WAIT_SETUP
	movwf	ctrl_trf_state
	movlw	MUID_NULL		; Set owner to NULL
	movwf	ctrl_trf_session_owner
	clrf	wCount
	clrf	wCount + 1
	rcall	USBCheckStdRequest
	movlb	high 0x400		; Point to proper bank
	movlw	MUID_NULL
	cpfseq	ctrl_trf_session_owner
	bra	USBCtrlEPServiceComplete
#ifdef USB_USE_HID
	rcall	USBCheckHIDRequest
#endif ; USB_USE_HID
#ifdef USB_USE_CDC
	rcall	USBCheckCDCRequest
#endif ; USB_USE_CDC
	bra	USBCtrlEPServiceComplete


; From usbctrltrf.c line 176
;/******************************************************************************
; * Function:        void USBCtrlTrfOutHandler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine handles an OUT transaction according to
; *                  which control transfer state is currently active.
; *
; * Note:            Note that if the the control transfer was from
; *                  host to device, the session owner should be notified
; *                  at the end of each OUT transaction to service the
; *                  received data.
; *
; *****************************************************************************/
USBCtrlTrfOutHandler
	movlb	high 0x400		; Point to proper bank
	movlw	CTRL_TRF_RX
	cpfseq	ctrl_trf_state
	bra	USBPrepareForNextSetupTrf
	rcall	USBCtrlTrfRxService
	movlb	high 0x400		; Point to proper bank
	movlw	_USIE|_DAT1|_DTSEN
	btfsc	ep0Bo + Stat, DTS
	movlw	_USIE|_DAT0|_DTSEN
	movwf	ep0Bo + Stat
	return


; From usbctrltrf.c line 221
;/******************************************************************************
; * Function:        void USBCtrlTrfInHandler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine handles an IN transaction according to
; *                  which control transfer state is currently active.
; *
; *
; * Note:            A Set Address Request must not change the acutal address
; *                  of the device until the completion of the control
; *                  transfer. The end of the control transfer for Set Address
; *                  Request is an IN transaction. Therefore it is necessary
; *                  to service this unique situation when the condition is
; *                  right. Macro mUSBCheckAdrPendingState is defined in
; *                  usb9.h and its function is to specifically service this
; *                  event.
; *****************************************************************************/
USBCtrlTrfInHandler
	movlb	high 0x400		; Point to proper bank
	movlw	ADR_PENDING_STATE	; Must check if in ADR_PENDING_STATE
	cpfseq	usb_device_state
	bra	USBCtrlTrfInHandler1
	movf	SetupPkt + bDevADR, W
#if (UADDR < 0xf60)
	BANKSEL	UADDR
#endif
	movwf	UADDR
	movlb	high 0x400		; Point to proper bank
	movlw	ADDRESS_STATE		; If UADDR > 0
	btfsc	STATUS, Z
	movlw	DEFAULT_STATE
	movwf	usb_device_state
USBCtrlTrfInHandler1
	movlw	CTRL_TRF_TX
	cpfseq	ctrl_trf_state
	bra	USBPrepareForNextSetupTrf
	rcall	USBCtrlTrfTxService
	movlb	high 0x400		; Point to proper bank
	movlw	SHORT_PKT_SENT		; 1.2
	cpfseq	short_pkt_status	; 1.2
	bra	USBCtrlTrfInHandler2	; 1.2
	movlw	_USIE|_BSTALL		; 1.2 If a short packet has been sent, don't want to send any more,
	movwf	ep0Bi + Stat		; 1.2 stall next time if host is still trying to read.
	return				; 1.2
USBCtrlTrfInHandler2			; 1.2
	movlw	_USIE|_DAT1|_DTSEN
	btfsc	ep0Bi + Stat, DTS
	movlw	_USIE|_DAT0|_DTSEN
	movwf	ep0Bi + Stat
	return


; From usbctrltrf.c line 260
;/******************************************************************************
; * Function:        void USBCtrlTrfTxService(void)
; *
; * PreCondition:    pSrc, wCount, and usb_stat.ctrl_trf_mem are setup properly.
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine should be called from only two places.
; *                  One from USBCtrlEPServiceComplete() and one from
; *                  USBCtrlTrfInHandler(). It takes care of managing a
; *                  transfer over multiple USB transactions.
; *
; * Note:            Copies one packet-ful of data pSrc (either ROM or RAM) to
; *                  EP0 IN buffer. It then updates pSrc to be ready for next
; *                  piece.
; *                  This routine works with isochronous endpoint larger than
; *                  256 bytes and is shown here as an example of how to deal
; *                  with BC9 and BC8. In reality, a control endpoint can never
; *                  be larger than 64 bytes.
; *****************************************************************************/
USBCtrlTrfTxService
	movlb	high 0x400		; Point to proper bank
	movf	wCount, W		; Preset wCount bytes to send
	movwf	usb_temp
	movf	wCount + 1, W
	movwf	usb_temp + 1
	sublw	high EP0_BUFF_SIZE	; Check for count less than maximium length
	bnz	USBCtrlTrfTxServiceNotEQ	; Not equal so we don't need to check the bottom byte
	movf	wCount, W
	sublw	low EP0_BUFF_SIZE
	bz	USBCtrlTrfTxServiceSub	; Equal to
USBCtrlTrfTxServiceNotEQ
	bnc	USBCtrlTrfTxServiceMax	; Greater than
	movlw	SHORT_PKT_NOT_USED	; 1.2
	cpfseq	short_pkt_status	; 1.2
	bra	USBCtrlTrfTxServiceNotEQ1	; 1.2
	movlw	SHORT_PKT_PENDING	; 1.2
	movwf	short_pkt_status	; 1.2
        bra	USBCtrlTrfTxServiceSub	; 1.2
USBCtrlTrfTxServiceNotEQ1		; 1.2
	movlw	SHORT_PKT_PENDING	; 1.2
	cpfseq	short_pkt_status	; 1.2
        bra	USBCtrlTrfTxServiceSub	; 1.2
	movlw	SHORT_PKT_SENT		; 1.2
	movwf	short_pkt_status	; 1.2
        bra	USBCtrlTrfTxServiceSub
USBCtrlTrfTxServiceMax
	movlw	low EP0_BUFF_SIZE	; Send buffer full of bytes
	movwf	usb_temp
	movlw	high EP0_BUFF_SIZE
	movwf	usb_temp + 1
USBCtrlTrfTxServiceSub
	movf	usb_temp, W		; Subtract actual bytes to be sent from the buffer
	movwf	ep0Bi + Cnt		; Save low number of bytes to send while we're here
	subwf	wCount, F
	movf	usb_temp + 1, W
	subwfb	wCount + 1, F
	movf	ep0Bi + Stat, W		; Get full Stat byte
	andlw	0xfc			; Clear bottom bits
	iorwf	usb_temp + 1, W		; Put in high bits of bytes to send
	movwf	ep0Bi + Stat		; Save it out
	lfsr	2, CtrlTrfData		; Set destination pointer
	movf	usb_temp + 1, W		; Check high byte for 0
	bnz	USBCtrlTrfTxServiceRomRam	; High byte not 0, must have something to do
	movf	usb_temp, W		; Check low byte for 0
	bz	USBCtrlTrfTxServiceExit	; If both 0 then nothing to send this time
USBCtrlTrfTxServiceRomRam
	btfss	usb_stat, ctrl_trf_mem	; ROM or RAM?
	bra	USBCtrlTrfTxServiceRam	; RAM
	movff	pSrc, TBLPTRL		; Move source pointer to TBLPTR
	movff	pSrc + 1, TBLPTRH
	clrf	TBLPTRU
USBCtrlTrfTxServiceRomLoop
	tblrd	*+
	movff	TABLAT, POSTINC2	; Copy one buffer to the other
	tblrd	*+			; Skip high location
	decf	usb_temp, F		; Count down number of bytes
	bnz	USBCtrlTrfTxServiceRomLoop
	decf	usb_temp + 1, F
	bc	USBCtrlTrfTxServiceRomLoop
	movff	TBLPTRL, pSrc		; Update source pointer
	movff	TBLPTRH, pSrc + 1
	return
USBCtrlTrfTxServiceRam
	movff	pSrc, FSR1L		; Move source pointer to FSR1
	movff	pSrc + 1, FSR1H
USBCtrlTrfTxServiceRamLoop
	movff	POSTINC1, POSTINC2	; Copy one buffer to the other
	decf	usb_temp, F		; Count down number of bytes
	bnz	USBCtrlTrfTxServiceRamLoop
	decf	usb_temp + 1, F
	bc	USBCtrlTrfTxServiceRamLoop
	movff	FSR1L, pSrc		; Update source pointer
	movff	FSR1H, pSrc + 1
USBCtrlTrfTxServiceExit
	return
	

; From usbctrltrf.c line 330
;/******************************************************************************
; * Function:        void USBCtrlTrfRxService(void)
; *
; * PreCondition:    pDst and wCount are setup properly.
; *                  pSrc is always &CtrlTrfData
; *                  usb_stat.ctrl_trf_mem is always _RAM.
; *                  wCount should be set to 0 at the start of each control
; *                  transfer.
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        Transfers bytes received, at EP0 OUT buffer CtrlTrfData,
; *                  to user's buffer pointed by pDst.
; *                  This routine only knows how to handle raw byte data.
; *                  HIDmaker handles transferring and unpacking by a callback
; *                  function in the generated main program, called from here.
; *
; * Note:            None
; *****************************************************************************/
USBCtrlTrfRxService
	movlb	high 0x400		; Point to proper bank
	movf	ep0Bo + Cnt, W		; Get low number of bytes to read
	movwf	usb_temp		; usb_temp & usb_temp + 1 are bytes to read
	addwf	wCount, F		; Accumulate total number of bytes read
	movf	ep0Bo + Stat, W		; Get high bits to read
	andlw	0x03			; Mask to the count
	movwf	usb_temp + 1		; Save to high byte of bytes to read
	addwfc	wCount + 1, F		; Add overflow from low byte (C) and high byte to total number
	lfsr	1, CtrlTrfData		; Point FSR1 to source
	movff	pDst, FSR2L		; Move destination pointer to FSR2
	movff	pDst + 1, FSR2H
	movf	usb_temp + 1, W		; Check high byte for 0
	bnz	USBCtrlTrfRxServiceLoop	; High byte not 0, must have something to do
	movf	usb_temp, W		; Check low byte for 0
	bz	USBCtrlTrfRxServiceExit	; If both 0 then nothing to send this time
USBCtrlTrfRxServiceLoop
	movff	POSTINC1, POSTINC2	; Copy one buffer to the other
	decf	usb_temp, F		; Count down number of bytes
	bnz	USBCtrlTrfRxServiceLoop
	decf	usb_temp + 1, F
	bc	USBCtrlTrfRxServiceLoop
	movff	FSR2L, pDst		; Update destination pointer
	movff	FSR2H, pDst + 1
USBCtrlTrfRxServiceExit
	return


; From usbctrltrf.c line 382
;/******************************************************************************
; * Function:        void USBCtrlEPServiceComplete(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine wrap up the ramaining tasks in servicing
; *                  a Setup Request. Its main task is to set the endpoint
; *                  controls appropriately for a given situation. See code
; *                  below.
; *                  There are three main scenarios:
; *                  a) There was no handler for the Request, in this case
; *                     a STALL should be sent out.
; *                  b) The host has requested a read control transfer,
; *                     endpoints are required to be setup in a specific way.
; *                  c) The host has requested a write control transfer, or
; *                     a control data stage is not required, endpoints are
; *                     required to be setup in a specific way.
; *
; *                  Packet processing is resumed by clearing PKTDIS bit.
; *
; * Note:            None
; *****************************************************************************/
USBCtrlEPServiceComplete
	movlb	high 0x400		; Point to proper bank
; PKTDIS bit is set when a Setup Transaction is received.
; Clear to resume packet processing.
	bcf	UCON, PKTDIS		; Copied to beginning, per errata
	movlw	MUID_NULL
	cpfseq	ctrl_trf_session_owner
	bra	USBCtrlEPServiceComplete1
;
; No handlers claimed ownership of this Setup packet.
; If no one knows how to service this request then stall.
; Must also prepare EP0 to receive the next SETUP transaction.
	movlw	EP0_BUFF_SIZE
	movwf	ep0Bo + Cnt
	movlw	low SetupPkt
	movwf	ep0Bo + ADRL
	movlw	high SetupPkt
	movwf	ep0Bo + ADRH
	movlw	_USIE|_BSTALL
	movwf	ep0Bo + Stat
;	movlw	_USIE|_BSTALL
	movwf	ep0Bi + Stat
	return
;
; A module has claimed ownership of the control transfer session.
USBCtrlEPServiceComplete1
	btfss	SetupPkt, DataDir
	bra	USBCtrlEPServiceComplete2
	movf	wCount + 1, W		; Make sure count does not exceed max length requested by Host
	subwf	SetupPkt + wLength + 1, W
	bnc	USBCtrlEPServiceCompleteCopy
	bnz	USBCtrlEPServiceComplete11
	movf	wCount, W
	subwf	SetupPkt + wLength, W
	bc	USBCtrlEPServiceComplete11
USBCtrlEPServiceCompleteCopy
	movff	SetupPkt + wLength, wCount	; Set count to maximum
	movff	SetupPkt + wLength + 1, wCount + 1
;
; Setup packet's data direction is "Device to Host"
USBCtrlEPServiceComplete11
	rcall	USBCtrlTrfTxService    ; Actually copy the data to EP0 IN buffer
	movlb	high 0x400		; Point to proper bank
	movlw	CTRL_TRF_TX
	movwf	ctrl_trf_state
; Control Read:
; <SETUP[0]><IN[1]><IN[0]>...<OUT[1]> | <SETUP[0]>
; 1. Prepare OUT EP to respond to early termination
;
; NOTE:
; If something went wrong during the control transfer,
; the last status stage may not be sent by the host.
; When this happens, two different things could happen
; depending on the host.
; a) The host could send out a RESET.
; b) The host could send out a new SETUP transaction
;    without sending a RESET first.
; To properly handle case (b), the OUT EP must be setup
; to receive either a zero length OUT transaction, or a
; new SETUP transaction.
;
; Since the SETUP transaction requires the DTS bit to be
; DAT0 while the zero length OUT status requires the DTS
; bit to be DAT1, the DTS bit check by the hardware should
; be disabled. This way the SIE could accept either of
; the two transactions.
;
; Furthermore, the Cnt byte should be set to prepare for
; the SETUP data (8-byte or more), and the buffer address
; should be pointed to SetupPkt.
	movlw	EP0_BUFF_SIZE
	movwf	ep0Bo + Cnt
	movlw	low SetupPkt
	movwf	ep0Bo + ADRL
	movlw	high SetupPkt
	movwf	ep0Bo + ADRH
	movlw	_USIE			; Note: DTSEN is 0!
	movwf	ep0Bo + Stat
; 2. Prepare IN EP to transfer data, Cnt should have
;    been initialized by responsible request owner.
	movlw	low CtrlTrfData
	movwf	ep0Bi + ADRL
	movlw	high CtrlTrfData
	movwf	ep0Bi + ADRH
	movlw	_USIE|_DAT1|_DTSEN
	movwf	ep0Bi + Stat
	return
;
; Setup packet's data direction is "Host to Device"
USBCtrlEPServiceComplete2
	movlw	CTRL_TRF_RX
	movwf	ctrl_trf_state
; Control Write:
; <SETUP[0]><OUT[1]><OUT[0]>...<IN[1]> | <SETUP[0]>
;
; 1. Prepare IN EP to respond to early termination
;
;    This is the same as a Zero Length Packet Response
;    for control transfer without a data stage
	clrf	ep0Bi + Cnt
	movlw	_USIE|_DAT1|_DTSEN
	movwf	ep0Bi + Stat
; 2. Prepare OUT EP to receive data.
	movlw	EP0_BUFF_SIZE
	movwf	ep0Bo + Cnt
	movlw	low CtrlTrfData
	movwf	ep0Bo + ADRL
	movlw	high CtrlTrfData
	movwf	ep0Bo + ADRH
	movlw	_USIE|_DAT1|_DTSEN
	movwf	ep0Bo + Stat
	return


; From usbctrltrf.c line 490
;/******************************************************************************
; * Function:        void USBPrepareForNextSetupTrf(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        The routine forces EP0 OUT to be ready for a new Setup
; *                  transaction, and forces EP0 IN to be owned by CPU.
; *
; * Note:            None
; *****************************************************************************/
USBPrepareForNextSetupTrf
	movlb	high 0x400		; Point to proper bank
	movlw	CTRL_TRF_RX		; 1.2
	cpfseq	ctrl_trf_state		; 1.2
	bra	USBPrepareForNextSetupTrf1	; 1.2
	btfss	UCON, PKTDIS		; 1.2
	bra	USBPrepareForNextSetupTrf1	; 1.2
	movlw	EP0_BUFF_SIZE		; 1.2
	cpfseq	ep0Bo + Cnt		; 1.2
	bra	USBPrepareForNextSetupTrf1	; 1.2
	movf	ep0Bo + Stat, W		; 1.2
	andlw	0x3c			; 1.2 Mask to PID
	sublw	(SETUP_TOKEN) << 2	; 1.2
	bnz	USBPrepareForNextSetupTrf1	; 1.2
	btfsc	ep0Bo + Stat, UOWN	; 1.2
	bra	USBPrepareForNextSetupTrf1	; 1.2
; The Setup data was written to the CtrlTrfData buffer, must copy
; it back to the SetupPkt buffer so that it can be processed correctly
; by USBCtrlTrfSetupHandler().
	lfsr	1, CtrlTrfData		; 1.2
	lfsr	2, SetupPkt		; 1.2
	movlw	EP0_BUFF_SIZE		; 1.2
USBPrepareForNextSetupTrfLoop		; 1.2
	movff	POSTINC1, POSTINC2	; 1.2
	decfsz	WREG, F			; 1.2
	bra	USBPrepareForNextSetupTrfLoop	; 1.2
	movlw	low SetupPkt		; 1.2
	movwf	ep0Bo + ADRL		; 1.2
	movlw	high SetupPkt		; 1.2
	movwf	ep0Bo + ADRH		; 1.2
	return				; 1.2
USBPrepareForNextSetupTrf1		; 1.2
	movlw	WAIT_SETUP
	movwf	ctrl_trf_state
	movlw	EP0_BUFF_SIZE
	movwf	ep0Bo + Cnt
	movlw	low SetupPkt
	movwf	ep0Bo + ADRL
	movlw	high SetupPkt
	movwf	ep0Bo + ADRH
	movlw	_USIE|_DAT0|_DTSEN|_BSTALL	; 1.2 EP0 buff dsc init
	movwf	ep0Bo + Stat
	movlw	_UCPU			; EP0 IN buffer initialization
	movwf	ep0Bi + Stat
	return



; From usbdrv.c line ???
;/******************************************************************************
; * Function:        void InitializeUSBDriver(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine initializes variables used by the USB library
; *                  routines.
; *
; * Note:            None
; *****************************************************************************/
USBDeviceInit
InitializeUSBDriver
#if (UCFG < 0xf60)
	BANKSEL	UCFG
#endif
	movlw	UCFG_VAL
	movwf	UCFG
	movlb	high 0x400		; Point to proper bank
	movlw	DETACHED_STATE
	movwf	usb_device_state
	clrf	usb_stat
	clrf	usb_active_cfg
#ifdef USB_USE_HID
	rcall	HIDInitEP
#endif
#ifdef USB_USE_CDC
	rcall	CDCInitEP
#endif
	return

	
; From usbdrv.c line 76
;/******************************************************************************
; * Function:        void USBCheckBusStatus(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine enables/disables the USB module by monitoring
; *                  the USB power signal.
; *
; * Note:            None
; *****************************************************************************/
USBCheckBusStatus
	movlb	high 0x400		; Point to proper bank
; Bus Attachment & Detachment Detection
; usb_bus_sense is an i/o pin defined in io_cfg.h
#ifdef USE_USB_BUS_SENSE_IO
	btfss	usb_bus_sense		; Is USB bus attached?
	bra	USBCheckBusStatusDetached	; No
#endif
	btfss	UCON, USBEN		; Is the module off?
	rcall	USBModuleEnable		; Is off, enable it
#ifdef USE_USB_BUS_SENSE_IO
	bra	USBCheckBusStatus1
USBCheckBusStatusDetached
	btfsc	UCON, USBEN		; Is the module on?
	rcall	USBModuleDisable	; Is on, disable it
#endif
;
; After enabling the USB module, it takes some time for the voltage
; on the D+ or D- line to rise high enough to get out of the SE0 condition.
; The USB Reset interrupt should not be unmasked until the SE0 condition is
; cleared. This helps preventing the firmware from misinterpreting this
; unique event as a USB bus reset from the USB host.
USBCheckBusStatus1
	movlw	ATTACHED_STATE
	cpfseq	usb_device_state
	return
	btfsc	UCON, SE0
	return
	clrf	UIR			; Clear all USB interrupts
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	clrf	UIE			; Mask all USB interrupts
	bsf	UIE, URSTIE		; Unmask RESET interrupt
	bsf	UIE, IDLEIE		; Unmask IDLE interrupt
	movlb	high 0x400		; Point to proper bank
	movlw	POWERED_STATE
	movwf	usb_device_state
	return


; From usbdrv.c line 135
USBModuleEnable
	clrf	UCON
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	clrf	UIE			; Mask all USB interrupts
	bsf	UCON, USBEN		; Enable module & attach to bus
	movlb	high 0x400		; Point to proper bank
	movlw	ATTACHED_STATE
	movwf	usb_device_state
	return


; From usbdrv.c line 192
USBSoftDetach
; From usbdrv.c line 161
USBModuleDisable
	clrf	UCON			; Disable module & detach from bus
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	clrf	UIE			; Mask all USB interrupts
	movlb	high 0x400		; Point to proper bank
	movlw	DETACHED_STATE
	movwf	usb_device_state
	return


; From usbdrv.c line 215
;/******************************************************************************
; * Function:        void USBDriverService(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine is the heart of this firmware. It manages
; *                  all USB interrupts.
; *
; * Note:            Device state transitions through the following stages:
; *                  DETACHED -> ATTACHED -> POWERED -> DEFAULT ->
; *                  ADDRESS_PENDING -> ADDRESSED -> CONFIGURED -> READY
; *****************************************************************************/
USBDeviceTasks
	rcall	USBCheckBusStatus
USBDriverService
	movlb	high 0x400		; Point to proper bank
	movlw	DETACHED_STATE
	subwf	usb_device_state, W
	bz	USBDriverServiceExit	; Pointless to continue servicing
					; if USB cable is not even attached.
;
; Task A: Service USB Activity Interrupt
	btfss	UIR, ACTVIF
	bra	USBDriverService1
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	btfsc	UIE, ACTVIE
	rcall	USBWakeFromSuspend
;
USBDriverService1
	btfsc	UCON, SUSPND		; Are we suspended?
	return				; Pointless to continue servicing if the device is in suspend mode.
;
; Task B: Service USB Bus Reset Interrupt.
; When bus reset is received during suspend, ACTVIF will be set first,
; once the UCONbits.SUSPND is clear, then the URSTIF bit will be asserted.
; This is why URSTIF is checked after ACTVIF.
	btfss	UIR, URSTIF        ; USB Bus Reset Interrupt?
	bra	USBDriverService2
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	btfsc	UIE, URSTIE
	rcall	USBProtocolResetHandler
;
; Task C: Check & service other USB interrupts
USBDriverService2
	btfss	UIR, IDLEIF
	bra	USBDriverService3
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	btfsc	UIE, IDLEIE
	rcall	USBSuspend
USBDriverService3
	btfss	UIR, SOFIF
	bra	USBDriverService4
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	btfsc	UIE, SOFIE
	rcall	USB_SOF_Handler
USBDriverService4
	btfss	UIR, STALLIF
	bra	USBDriverService5
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	btfsc	UIE, STALLIE
	rcall	USBStallHandler
USBDriverService5
	btfss	UIR, UERRIF
	bra	USBDriverService6
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	btfsc	UIE, UERRIE
	rcall	USBErrorHandler
;
; Pointless to continue servicing if the host has not sent a bus reset.
; Once bus reset is received, the device transitions into the DEFAULT
;     * state and is ready for communication.
USBDriverService6
	movlb	high 0x400		; Point to proper bank
	movlw	DEFAULT_STATE
	subwf	usb_device_state, W
	bnc	USBDriverServiceExit
;
; Task D: Servicing USB Transaction Complete Interrupt
	movlw	4			; 1.2 We're gonna try this up to 4 times
	movwf	bTRNIFCount		; 1.2
USBDriverService7			; 1.2
	btfss	UIR, TRNIF
	return
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	btfss	UIE, TRNIE
	return
;
; USBCtrlEPService only services transactions over EP0.
; It ignores all other EP transactions.
	rcall	USBCtrlEPService
; Other EPs can be serviced later by responsible device class firmware.
; Each device driver knows when an OUT or IN transaction is ready by
; checking the buffer ownership bit.
; An OUT EP should always be owned by SIE until the data is ready.
; An IN EP should always be owned by CPU until the data is ready.
;
; Because of this logic, it is not necessary to save the USTAT value
; of non-EP0 transactions.
	movlb	high 0x400		; Point to proper bank
	btfss	trnifcleared, 0		; 1.2 If TRNIF not cleared in USBCtrlEPService, do it now
	bcf	UIR, TRNIF
	nop				; 1.2
	nop				; 1.2
	nop				; 1.2
	nop				; 1.2
;	nop				; 1.2
;	nop				; 1.2
	decfsz	bTRNIFCount, F		; 1.2
	bra	USBDriverService7	; 1.2
USBDriverServiceExit
	return

	
; From usbdrv.c line 301
;/******************************************************************************
; * Function:        void USBSuspend(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:
; *
; * Note:            None
; *****************************************************************************/
USBSuspend
; NOTE: Do not clear UIRbits.ACTVIF here!
; Reason:
; ACTVIF is only generated once an IDLEIF has been generated.
; This is a 1:1 ratio interrupt generation.
; For every IDLEIF, there will be only one ACTVIF regardless of
; the number of subsequent bus transitions.
;
; If the ACTIF is cleared here, a problem could occur when:
; [       IDLE       ][bus activity ->
; <--- 3 ms ----->     ^
;                ^     ACTVIF=1
;                IDLEIF=1
;  #           #           #           #   (#=Program polling flags)
;                          ^
;                          This polling loop will see both
;                          IDLEIF=1 and ACTVIF=1.
;                          However, the program services IDLEIF first
;                          because ACTIVIE=0.
;                          If this routine clears the only ACTIVIF,
;                          then it can never get out of the suspend
;                          mode.
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	bsf	UIE, ACTVIE		; Enable bus activity interrupt
	bcf	UIR, IDLEIF
	bsf	UCON, SUSPND		; Put USB module in power conserve
; At this point the PIC can go into sleep,idle, or
; switch to a slower clock, etc.
	return


; From usbdrv.c line 353
;/******************************************************************************
; * Function:        void USBWakeFromSuspend(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:
; *
; * Note:            None
; *****************************************************************************/
USBWakeFromSuspend
; If using clock switching, this is the place to restore the
; original clock frequency.
	bcf	UCON, SUSPND
#if (UIE < 0xf60)
	BANKSEL	UIE
#endif
	bcf	UIE, ACTVIE
USBWakeFromSuspendLoop			; 1.2
	bcf	UIR, ACTVIF
	btfsc	UIR, ACTVIF		; 1.2
	bra	USBWakeFromSuspendLoop	; 1.2
	return


; From usbdrv.c line 402
;/******************************************************************************
; * Function:        void USBRemoteWakeup(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This function should be called by user when the device
; *                  is waken up by an external stimulus other than ACTIVIF.
; *                  Please read the note below to understand the limitations.
; *
; * Note:            The modifiable section in this routine should be changed
; *                  to meet the application needs. Current implementation
; *                  temporary blocks other functions from executing for a
; *                  period of 1-13 ms depending on the core frequency.
; *
; *                  According to USB 2.0 specification section 7.1.7.7,
; *                  "The remote wakeup device must hold the resume signaling
; *                  for at lest 1 ms but for no more than 15 ms."
; *                  The idea here is to use a delay counter loop, using a
; *                  common value that would work over a wide range of core
; *                  frequencies.
; *                  That value selected is 1800. See table below:
; *                  ==========================================================
; *                  Core Freq(MHz)      MIP         RESUME Signal Period (ms)
; *                  ==========================================================
; *                      48              12          1.05
; *                       4              1           12.6
; *                  ==========================================================
; *                  * These timing could be incorrect when using code
; *                    optimization or extended instruction mode,
; *                    or when having other interrupts enabled.
; *                    Make sure to verify using the MPLAB SIM's Stopwatch
; *****************************************************************************/
USBRemoteWakeup
	movlb	high 0x400		; Point to proper bank
	btfss	usb_stat, RemoteWakeup	; Check if RemoteWakeup function has been enabled by the host.
	return				; No
	bcf	UCON, SUSPND		; 1.2 Unsuspend USB module
	bsf	UCON, RESUME		; Start RESUME signaling
	movlw	0x10			; Set RESUME line for 1-13 ms
	movwf	FSR2H			; Using FSR2 as temp
	clrf	FSR2L
USBRemoteWakeupLoop
	decfsz	FSR2L, F
	bra	USBRemoteWakeupLoop
	decfsz	FSR2H, F
	bra	USBRemoteWakeupLoop
	bcf	UCON, RESUME
	return


; From usbdrv.c line 443
;/******************************************************************************
; * Function:        void USB_SOF_Handler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        The USB host sends out a SOF packet to full-speed devices
; *                  every 1 ms. This interrupt may be useful for isochronous
; *                  pipes. End designers should implement callback routine
; *                  as necessary.
; *
; * Note:            None
; *****************************************************************************/
USB_SOF_Handler
; Callback routine here
	bcf UIR, SOFIF
	return


; From usbdrv.c line 486
;/******************************************************************************
; * Function:        void USBStallHandler(void)
; *
; * PreCondition:    A STALL packet is sent to the host by the SIE.
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        The STALLIF is set anytime the SIE sends out a STALL
; *                  packet regardless of which endpoint causes it.
; *                  A Setup transaction overrides the STALL function. A stalled
; *                  endpoint stops stalling once it receives a setup packet.
; *                  In this case, the SIE will accepts the Setup packet and
; *                  set the TRNIF flag to notify the firmware. STALL function
; *                  for that particular endpoint pipe will be automatically
; *                  disabled (direction specific).
; *
; *                  There are a few reasons for an endpoint to be stalled.
; *                  1. When a non-supported USB request is received.
; *                     Example: GET_DESCRIPTOR(DEVICE_QUALIFIER)
; *                  2. When an endpoint is currently halted.
; *                  3. When the device class specifies that an endpoint must
; *                     stall in response to a specific event.
; *                     Example: Mass Storage Device Class
; *                              If the CBW is not valid, the device shall
; *                              STALL the Bulk-In pipe.
; *                              See USB Mass Storage Class Bulk-only Transport
; *                              Specification for more details.
; *
; * Note:            UEPn.EPSTALL can be scanned to see which endpoint causes
; *                  the stall event.
; *****************************************************************************/
USBStallHandler
; Does not really have to do anything here,
; even for the control endpoint.
; All BDs of Endpoint 0 are owned by SIE right now,
; but once a Setup Transaction is received, the ownership
; for EP0_OUT will be returned to CPU.
; When the Setup Transaction is serviced, the ownership
; for EP0_IN will then be forced back to CPU by firmware.
;
; NOTE: Above description is not quite true at this point.
;       It seems the SIE never returns the UOWN bit to CPU,
;       and a TRNIF is never generated upon successful
;       reception of a SETUP transaction.
;       Firmware work-around is implemented below.
;
#if (UEP0 < 0xf60)
	BANKSEL	UEP0
#endif
	btfss	UEP0, EPSTALL		; 1.2
	bra	USBStallHandlerExit	; 1.2
	movlb	high 0x400		; Point to proper bank
	movlw	_USIE			; 1.2
	cpfseq	ep0Bo + Stat		; 1.2
	bra	USBStallHandlerExit	; 1.2
	movlw	_USIE|_BSTALL		; 1.2
	cpfseq	ep0Bi + Stat		; 1.2
	bra	USBStallHandlerExit	; 1.2
	movlw	_USIE|_DAT0|DTSEN|_BSTALL	; 1.2
	movwf	ep0Bo + Stat		; 1.2
#if (UEP0 < 0xf60)
	BANKSEL	UEP0
#endif
	bcf	UEP0, EPSTALL
USBStallHandlerExit			; 1.2
	bcf	UIR, STALLIF
	return

; From usbdrv.c line 528
;/******************************************************************************
; * Function:        void USBErrorHandler(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        The purpose of this interrupt is mainly for debugging
; *                  during development. Check UEIR to see which error causes
; *                  the interrupt.
; *
; * Note:            None
; *****************************************************************************/
USBErrorHandler
	clrf	UEIR			; 1.2
	return


; From usbdrv.c line 555
;/******************************************************************************
; * Function:        void USBProtocolResetHandler(void)
; *
; * PreCondition:    A USB bus reset is received from the host.
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    Currently, this routine flushes any pending USB
; *                  transactions. It empties out the USTAT FIFO. This action
; *                  might not be desirable in some applications.
; *
; * Overview:        Once a USB bus reset is received from the host, this
; *                  routine should be called. It resets the device address to
; *                  zero, disables all non-EP0 endpoints, initializes EP0 to
; *                  be ready for default communication, clears all USB
; *                  interrupt flags, unmasks applicable USB interrupts, and
; *                  reinitializes internal state-machine variables.
; *
; * Note:            None
; *****************************************************************************/
USBProtocolResetHandler
	clrf	UEIR			; Clear all USB error flags
	clrf	UIR			; Clears all USB interrupts
#if (UEIE < 0xf60)
	BANKSEL	UEIE
#endif
	movlw	0x9f			; Unmask all USB error interrupts
	movwf	UEIE
	movlw	0x7b			; Enable all interrupts except ACTVIE
	movwf	UIE
	clrf	UADDR			; Reset to default address
	lfsr	2, UEP1			; Reset all non-EP0 UEPn registers
	movlw	15
USBProtocolResetHandlerClearLoop
	clrf	POSTINC2
	decfsz	WREG, F
	bra	USBProtocolResetHandlerClearLoop
	movlw	EP_CTRL|HSHK_EN		; Init EP0 as a Ctrl EP
	movwf	UEP0
	bra	USBProtocolResetHandlerFlushL1	; Simulate while
USBProtocolResetHandlerFlushLoop
	bcf	UIR, TRNIF		; Flush any pending transactions
	nop				; 1.2
	nop				; 1.2
	nop				; 1.2
	nop				; 1.2
	nop				; 1.2
	nop				; 1.2
USBProtocolResetHandlerFlushL1
	btfsc	UIR, TRNIF
	bra	USBProtocolResetHandlerFlushLoop
	bcf	UCON, PKTDIS		; Make sure packet processing is enabled
  	rcall	USBPrepareForNextSetupTrf
	movlb	high 0x400		; Point to proper bank
	bcf	usb_stat, RemoteWakeup	; Default status flag to disable
	clrf	usb_active_cfg		; Clear active configuration
	movlw	DEFAULT_STATE
  	movwf	usb_device_state
	return



#ifdef USB_USE_HID

; From hid.c  line 72
;/******************************************************************************
; * Function:        void USBCheckHIDRequest(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine checks the setup data packet to see if it
; *                  knows how to handle it
; *
; * Note:            None
; *****************************************************************************/
USBCheckHIDRequest
	movlb	high 0x400		; Point to proper bank
	movf	SetupPkt, W		; Recipient = RCPT_INTF?
	andlw	0x1f			; Mask to lower 5 bits
	sublw	RCPT_INTF
	bnz	USBCheckHIDRequestExit	; No
	movlw	HID_INTF_ID		; IntfID = HID_INTF_ID?
	cpfseq	SetupPkt + bIntfID
USBCheckHIDRequestExit
	return				; No
;
; There are two standard requests that hid.c may support.
; 1. GET_DSC(DSC_HID,DSC_RPT,DSC_PHY);
; 2. SET_DSC(DSC_HID,DSC_RPT,DSC_PHY);
;
	movlw	GET_DSC			; Request = GET_DSC?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckHIDRequestClass	; No
	movlw	DSC_HID			; DscType = DSC_HID?
	cpfseq	SetupPkt + bDscType
	bra	USBCheckHIDRequest1	; No
	mSetSourcePointer HIDDescriptor1
	mGetRomTableCount		; Set wCount
	bsf	usb_stat, ctrl_trf_mem	; Indicate ROM
	bra	USBHIDSetSessionOwner
USBCheckHIDRequest1
	movlw	DSC_RPT			; DscType = DSC_RPT?
	cpfseq	SetupPkt + bDscType
	bra	USBCheckHIDRequest2	; No
	mSetSourcePointer ReportDescriptor1
	movlw	low (ReportDescriptor1Len)	; Set wCount
	movwf	TBLPTRL
	movlw	high (ReportDescriptor1Len)
	movwf	TBLPTRH
	clrf	TBLPTRU
        tblrd   *+			; Read count low
	movff	TABLAT, wCount
        tblrd   *+			; Skip next
        tblrd   *			; Read count high
	movff	TABLAT, wCount + 1
	bsf	usb_stat, ctrl_trf_mem	; Indicate ROM
	bra	USBHIDSetSessionOwner
USBCheckHIDRequest2
;	movlw	DSC_PHY			; DscType = DSC_PHY?
;	cpfseq	SetupPkt + bDscType
;	return				; No
USBCheckHIDRequestClass
	movf	SetupPkt, W		; RequestType = CLASS?
	andlw	0x60			; Mask to proper bits
	sublw	(CLASS) << 5
	bnz	USBCheckHIDRequestExit	; No
;	movlw	GET_REPORT		; Request = GET_REPORT?
;	subwf	SetupPkt + bRequest, W
;	bz	HIDGetReportHandler	; Yes
;	movlw	SET_REPORT		; Request = SET_REPORT?
;	subwf	SetupPkt + bRequest, W
;	bz	HIDSetReportHandler	; Yes
	movlw	GET_IDLE		; Request = GET_IDLE?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckHIDRequestClass1	; No
	mSetSourcePointer idle_rate
	movlw	1
	movwf	wCount
	clrf	wCount + 1
	bcf	usb_stat, ctrl_trf_mem	; Indicate RAM
	bra	USBHIDSetSessionOwner
USBCheckHIDRequestClass1
	movlw	SET_IDLE		; Request = SET_IDLE?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckHIDRequestClass2	; No
	movff	SetupPkt + wValue + 1, idle_rate
	bra	USBHIDSetSessionOwner
USBCheckHIDRequestClass2
	movlw	GET_PROTOCOL		; Request = GET_PROTOCOL?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckHIDRequestClass3	; No
	mSetSourcePointer active_protocol
	movlw	1
	movwf	wCount
	clrf	wCount + 1
	bcf	usb_stat, ctrl_trf_mem	; Indicate RAM
	bra	USBHIDSetSessionOwner
USBCheckHIDRequestClass3
	movlw	SET_PROTOCOL		; Request = SET_PROTOCOL?
	cpfseq	SetupPkt + bRequest
	return				; No
	movff	SetupPkt + wValue, active_protocol
USBHIDSetSessionOwner
	movlw	MUID_HID
	movwf	ctrl_trf_session_owner
	return


; From hid.c  line 158
;/******************************************************************************
; * Function:        void HIDInitEP(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        HIDInitEP initializes HID endpoints, buffer descriptors,
; *                  internal state-machine, and variables.
; *                  It should be called after the USB host has sent out a
; *                  SET_CONFIGURATION request.
; *                  See USBStdSetCfgHandler() in usb9.c for examples.
; *
; * Note:            None
; *****************************************************************************/
HIDInitEP
	movlw	1			; Endpoint 1 Out
	movwf	FSR0L
	lfsr	1, hid_report_out	; FSR1 = endpoint buffer address
	movlw	HID_INT_OUT_EP_SIZE	; W = endpoint size
	rcall	InitEPOut		; Inititalize the endpoint

	movlw	1			; Endpoint 1 In
	movwf	FSR0L
	lfsr	1, hid_report_in	; FSR1 = endpoint buffer address
	movlw	HID_INT_IN_EP_SIZE	; W = endpoint size
	bra	InitEPIn		; Inititalize the endpoint

#endif	; USB_USE_HID



#ifdef USB_USE_CDC

;/******************************************************************************
; * Function:        void USBCheckCDCRequest(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        This routine checks the setup data packet to see if it
; *                  knows how to handle it
; *
; * Note:            None
; *****************************************************************************/
USBCheckCDCRequest
;    /*
;     * If request recipient is not an interface then return
;     */
	movlb	high 0x400		; Point to proper bank
	movf	SetupPkt, W		; Recipient = RCPT_INTF?
	andlw	0x1f			; Mask to lower 5 bits
	sublw	RCPT_INTF
	bnz	USBCheckCDCRequestExit	; No

;    /*
;     * If request type is not class-specific then return
;     */
	movf	SetupPkt, W		; RequestType = CLASS?
	andlw	0x60			; Mask to proper bits
	sublw	(CLASS) << 5
	bnz	USBCheckCDCRequestExit	; No

;    /*
;     * Interface ID must match interface numbers associated with
;     * CDC class, else return
;     */
	movlw	CDC_COMM_INTF_ID	; IntfID = CDC_COMM_INTF_ID?
	subwf	SetupPkt + bIntfID, W
	bz	USBCheckCDCRequest1	; Yes
	movlw	CDC_DATA_INTF_ID	; IntfID = CDC_DATA_INTF_ID?
	cpfseq	SetupPkt + bIntfID
USBCheckCDCRequestExit
	return				; No
    
USBCheckCDCRequest1
	movlw	SEND_ENCAPSULATED_COMMAND	; Request = SEND_ENCAPSULATED_COMMAND?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest2	; No
	mSetSourcePointer dummy_encapsulated_cmd_response
	bcf	usb_stat, ctrl_trf_mem	; Indicate RAM
	movlw	dummy_length
	movwf	wCount
	clrf	wCount + 1
	bra	USBCDCSetSessionOwner
USBCheckCDCRequest2
	movlw	GET_ENCAPSULATED_RESPONSE	; Request = GET_ENCAPSULATED_RESPONSE?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest3	; No
;       // Populate dummy_encapsulated_cmd_response first.
	mSetDestinationPointer dummy_encapsulated_cmd_response
	bra	USBCDCSetSessionOwner
USBCheckCDCRequest3
	movlw	SET_COMM_FEATURE	; Request = SET_COMM_FEATURE?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest4	; No
	return				; Optional
USBCheckCDCRequest4
	movlw	GET_COMM_FEATURE	; Request = GET_COMM_FEATURE?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest5	; No
	return				; Optional
USBCheckCDCRequest5
	movlw	CLEAR_COMM_FEATURE	; Request = CLEAR_COMM_FEATURE?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest6	; No
	return				; Optional
USBCheckCDCRequest6
	movlw	SET_LINE_CODING		; Request = SET_LINE_CODING?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest7	; No
	mSetDestinationPointer line_coding
	bra	USBCDCSetSessionOwner
USBCheckCDCRequest7
	movlw	GET_LINE_CODING		; Request = GET_LINE_CODING?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest8	; No
	; Abstract line coding information
	movlw	low 115200		; baud rate
	movwf	line_coding + dwDTERate
	movlw	high 115200
	movwf	line_coding + dwDTERate + 1
	movlw	upper 115200
	movwf	line_coding + dwDTERate + 2
	clrf	line_coding + dwDTERate + 3
	clrf	line_coding + bCharFormat	; 1 stop bit
	clrf	line_coding + bParityType	; None
	movlw	8
	movwf	line_coding + bDataBits	; 5,6,7,8, or 16
	mSetSourcePointer line_coding
	bcf	usb_stat, ctrl_trf_mem	; Indicate RAM
	movlw	LINE_CODING_LENGTH
	movwf	wCount
	clrf	wCount + 1
	bra	USBCDCSetSessionOwner
USBCheckCDCRequest8
	movlw	SET_CONTROL_LINE_STATE	; Request = SET_CONTROL_LINE_STATE?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest9	; No
	movff	SetupPkt + wValue, control_signal_bitmap
	bra	USBCDCSetSessionOwner
USBCheckCDCRequest9
	movlw	SEND_BREAK		; Request = SEND_BREAK?
	cpfseq	SetupPkt + bRequest
	bra	USBCheckCDCRequest10	; No
	return				; Optional
USBCheckCDCRequest10
	return				; Default
USBCDCSetSessionOwner
	movlw	MUID_CDC
	movwf	ctrl_trf_session_owner
	return


;/******************************************************************************
; * Function:        void CDCInitEP(void)
; *
; * PreCondition:    None
; *
; * Input:           None
; *
; * Output:          None
; *
; * Side Effects:    None
; *
; * Overview:        CDCInitEP initializes CDC endpoints, buffer descriptors,
; *                  internal state-machine, and variables.
; *                  It should be called after the USB host has sent out a
; *                  SET_CONFIGURATION request.
; *                  See USBStdSetCfgHandler() in usb9.c for examples.
; *
; * Note:            None
; *****************************************************************************/
CDCInitEP
	movlw	2			; Endpoint 2 In
	movwf	FSR0L
	lfsr	1, cdc_notice		; FSR1 = endpoint buffer address
	movlw	CDC_INT_EP_SIZE		; W = endpoint size
	rcall	InitEPIn		; Inititalize the endpoint

	movlw	3			; Endpoint 3
	movwf	FSR0L
	lfsr	1, cdc_data_rx		; FSR1 = endpoint buffer address
	movlw	CDC_BULK_OUT_EP_SIZE	; W = endpoint size
	rcall	InitEPOut		; Inititalize the endpoint

	movlw	3			; Endpoint 3 In
	movwf	FSR0L
	lfsr	1, cdc_data_tx		; FSR1 = endpoint buffer address
	movlw	CDC_BULK_IN_EP_SIZE	; W = endpoint size
	bra	InitEPIn		; Inititalize the endpoint

#endif	; USB_USE_CDC


; Generic code for use by all the classes

; InitEPIn
;  Generic initialize In endpoint
; Input:
;  FSR0L is endpoint number
;  FSR1 is endpoint buffer address
;  W is endpoint buffer size
; Returns:
;  Nada
; Uses
;  FSR0 is BDT pointer
;  FSR1 is endpoint buffer pointer
;  FSR2 is endpoint table pointer

InitEPIn
; Save maximum count at front of endpoint buffer and move buffer pointer up (no need to put in Cnt for In)
	movwf	POSTINC1		; Store maximum count at front of endpoint buffer and move up pointer

; Point FSR2 to endpoint table
	lfsr	2, UEP0
	movf	FSR0L, W		; Add in endpoint number
	addwf	FSR2L, F

; Enable In endpoint
	movlw	EP_IN|HSHK_EN		; Enable In pipe
	iorwf	INDF2, F

; Point FSR0 to endpoint BDT
	rlncf	FSR0L, W		; Endpoint number * 8
	rlncf	WREG, F
	rlncf	WREG, F
	lfsr	0, ep0Bi		; Point FSR0 to beginning of BDT area
	addwf	FSR0L, F		; Add endpoint offset to FSR0 (can't overflow to FSR0H)

; Set endpoint buffer address from FSR1
	movlw	ADRL			; Point to ADRL
	movff	FSR1L, PLUSW0
	movlw	ADRH			; Point to ADRH
	movff	FSR1H, PLUSW0

; Set endpoint status
	movlw	_UCPU|_DAT1		; Set transmit status
	movwf	INDF0			; Set Stat
	return


; InitEPOut
;  Generic initialize Out endpoint
; Input:
;  FSR0L is endpoint number
;  FSR1 is endpoint buffer address
;  W is endpoint buffer size
; Returns:
;  Nada
; Uses
;  FSR0 is BDT pointer
;  FSR1 is endpoint buffer pointer
;  FSR2 is endpoint table pointer

InitEPOut
; Save maximum count at front of endpoint buffer and move buffer pointer up
	movwf	POSTINC1		; Store maximum count at front of endpoint buffer and move up pointer

; Point FSR2 to endpoint table
	lfsr	2, UEP0
	movf	FSR0L, W		; Add in endpoint number
	addwf	FSR2L, F

; Enable Out endpoint
	movlw	EP_OUT|HSHK_EN		; Enable Out pipe
	iorwf	INDF2, F

; Point FSR0 to endpoint BDT
	rlncf	FSR0L, W		; Endpoint number * 8
	rlncf	WREG, F
	rlncf	WREG, F
	lfsr	0, ep0Bo		; Point FSR0 to beginning of BDT area
	addwf	FSR0L, F		; Add endpoint offset to FSR0 (can't overflow to FSR0H)

; Set endpoint buffer address from FSR1 + 1
	movlw	ADRL			; Point to ADRL
	movff	FSR1L, PLUSW0
	movlw	ADRH			; Point to ADRH
	movff	FSR1H, PLUSW0

; Set Cnt to maximum count
	movf	POSTDEC1, W		; Back up endpoint buffer pointer (no PREDEC1!)
	incf	FSR0L, F		; Point to Cnt
	movff	INDF1, POSTDEC0		; Set maximum count and point back to Stat

; Set endpoint status
	movlw	_USIE|_DAT0|_DTSEN	; Set receive status
	movwf	INDF0			; Set Stat
	return


; PutUSB
;  Generic fill In endpoint for TX
; Input:
;  FSR0L is endpoint number
;  FSR1 is source buffer pointer
;  W is count
; Returns:
;  FSR1 is updated source buffer pointer
;  W returns number sent
;  Carry is clear for buffer not available
; Uses:
;  FSR0 is BDT pointer
;  FSR1 is source buffer pointer
;  FSR2 is endpoint buffer pointer
;  R0 in BANKA is temporary length storage

PutUSB
	movwf	R0			; Save count

; Check to see if we're configured
	movlb	high 0x400		; Point to proper bank
	movlw	CONFIGURED_STATE	; We might not be configured yet
	subwf	usb_device_state, W
	movlw	0			; 0 characters sent, so far
	bcf	STATUS, C		; Clear Carry for possible error return
	bnz	PutUSBNotReady		; We're not configured

; Point FSR0 to requested endpoint In BDT
	rlncf	FSR0L, W		; Endpoint number * 8
	rlncf	WREG, F
	rlncf	WREG, F
	lfsr	0, ep0Bi		; Point FSR0 to beginning of BDT area
	addwf	FSR0L, F		; Add endpoint offset to FSR0 (can't overflow to FSR0H)

	movlw	0			; 0 characters sent, so far
	bcf	STATUS, C		; Clear Carry for possible error return
	btfsc	INDF0, UOWN		; Who owns the buffer (Stat, UOWN)?
PutUSBNotReady
	return				; Busy (we don't)

; Get endpoint buffer address to FSR2
	movlw	ADRL			; Point to ADRL
	movff	PLUSW0, FSR2L
	movlw	ADRH			; Point to ADRH
	movff	PLUSW0, FSR2H

	movlw	-1
	movf	PLUSW2, W		; Get maximum length from in front of endpoint buffer

	cpfslt	R0			; Find number of bytes to send this time
	movwf	R0			; Too big - send maximum allowed length

	incf	FSR0L, F		; Point to Cnt
	movf	R0, W			; Get number to send
	movwf	POSTDEC0		; Put length into Cnt and point back to Stat
	bz	PutUSBZero		; Zero to send

PutUSBRamLoop
	movff	POSTINC1, POSTINC2	; Copy source buffer to endpoint buffer
	decfsz	WREG, F			; Count down number of bytes to transfer
	bra	PutUSBRamLoop

PutUSBZero
	movlw	_DTSMASK		; Save only DTS bit
	andwf	INDF0, F
	btg	INDF0, DTS		; Toggle DTS bit
	movlw	_USIE|_DTSEN		; Turn ownership to SIE
	iorwf	INDF0, F
	movf	R0, W			; Return number of bytes sent
	bsf	STATUS, C		; Set Carry for non-error return
	return


; GetUSB
;  Generic get from Out endpoint for RX
; Input:
;  FSR0L is endpoint number
;  FSR1 is destination buffer pointer
;  W is max buffer length
; Returns:
;  FSR1 is updated destination buffer pointer
;  W returns number received
;  Carry is clear for buffer not available
; Uses
;  FSR0 is BDT pointer
;  FSR1 is destination buffer pointer
;  FSR2 is endpoint buffer pointer
;  R0 in BANKA is temporary length storage

GetUSB
	movwf	R0			; Save max buffer length

; Check to see if we're configured
	movlb	high 0x400		; Point to proper bank
	movlw	CONFIGURED_STATE	; We might not be configured yet
	subwf	usb_device_state, W
	movlw	0			; 0 characters received, so far
	bcf	STATUS, C		; Clear Carry for possible error return
	bnz	GetUSBNotReady		; We're not configured

; Point FSR0 to requested endpoint Out BDT
	rlncf	FSR0L, W		; Endpoint number * 8
	rlncf	WREG, F
	rlncf	WREG, F
	lfsr	0, ep0Bo		; Point FSR0 to beginning of BDT area
	addwf	FSR0L, F		; Add endpoint offset to FSR0 (can't overflow to FSR0H)

	movlw	0			; 0 characters received, so far
	bcf	STATUS, C		; Clear Carry for possible error return
	btfsc	INDF0, UOWN		; Who owns the buffer (Stat, UOWN)?
GetUSBNotReady
	return				; Busy (we don't)

; Get endpoint buffer address to FSR2
	movlw	ADRL			; Point to ADRL
	movff	PLUSW0, FSR2L
	movlw	ADRH			; Point to ADRH
	movff	PLUSW0, FSR2H

	movf	PREINC0, W		; Get Cnt
	cpfslt	R0			; Make sure it's not longer than the buffer
	movwf	R0			; It's OK, save returned length

	movlw	-1
	movf	PLUSW2, W		; Get maximum length from in front of endpoint buffer
	movwf	POSTDEC0		; Reset max length and point back to Stat

	movf	R0, W			; Get count to W
	bz	GetUSBZero		; Nothing received

GetUSBRamLoop
	movff	POSTINC2, POSTINC1	; Copy endpoint buffer to destination buffer
	decfsz	WREG, F			; Count down number of bytes
	bra	GetUSBRamLoop

GetUSBZero
	movlw	_DTSMASK		; Save only DTS bit
	andwf	INDF0, F
	btg	INDF0, DTS		; Toggle DTS bit
	movlw	_USIE|_DTSEN		; Turn ownership to SIE
	iorwf	INDF0, F
	movf	R0, W			; Return number of bytes received
	bsf	STATUS, C		; Set Carry for non-error return
	return
