USB_INT.A51 - common module
; This module is common to all of the examples.
; It services USB Requests from the SIE.
; Interpretation of the Output Reports is handled by MAIN
;
 	CSEG
ServiceSetupPacket:
	MOV	DPTR, #SETUPDAT		; Point to Setup Packet data
	MOVX	A, @DPTR		; Get the RequestType
	MOV	C, ACC.7		; Bit 7 = 1 means IO device needs to send data to PC Host
	MOV	SendData, C
	ANL	A, #01011100b		; IF RequestType[6.4.3.2] = 1 THEN goto BadRequest
	JNZ	BadRequest
	MOVX	A, @DPTR		; IF RequestType[1&0] = 1 THEN goto BadRequest
	MOV	C, ACC.0
	ANL	C, ACC.1
	JC	BadRequest
	JNB	ACC.5, NotB5		; IF RequestType[5] = 1 THEN RequestType[1,0] = [1,1]
	MOV	A, #00000011b
NotB5:	ANL	A, #00000011b		; Set CommandIndex[5,4] = RequestType[1,0]
	SWAP	A
	MOV	Temp, A			; Save HI nibble of CommandIndex
					; Set CommandIndex[3,0] = Request[3,0]
	INC	DPTR			; Point to Request
	MOVX	A, @DPTR
	ANL	A, #00001111b		; Only 13 are defined today, handle in table
	ORL	A, Temp	
	CALL	CorrectSubroutine	; goto CommandTable(CommandIndex)
					; Returns STALL=1 if a stall is required
	JB	STALL, BadRequest
       	JNB	SendData, HandShake
	JB	IsDescriptor, LoadSUDPTR; EZ-USB has a short cut for descriptors
                                        ; Send data in ReplyBuffer
	MOV	DPTR, #EP0InBuffer+2
	MOV	R0, #ReplyBuffer+3
	MOV	Temp, #3		; Copy maximum byte count
CopyRB:	MOV	A, @R0
	MOVX	@DPTR, A
	DEC	DPL
	DEC	R0
	DJNZ	Temp, CopyRB
	MOV	A, @R0 			; Get real byte count
SendEP0InBuffer:
	MOV	DPTR, #In0ByteCount
StartXfer:
	MOVX	@DPTR, A		; This write initiates the transfer
HandShake:				; Handshake with host
	MOV	Temp, #00000010b 	; Set HSNAK to tell the SIE that we're done
SetEP0Control:
	MOV	DPTR, #EP0Control
	MOVX	A, @DPTR
	ORL	A, Temp
	MOVX	@DPTR, A
	RET
LoadSUDPTR:			      	; Send the data pointed to by DPTR
	MOV	Temp, DPL
	MOV	A, DPH
	MOV	DPTR, #SUDPTR
	MOVX	@DPTR, A
	MOV	A, Temp
	INC	DPTR
	JMP	StartXfer
BadRequest:				; Invalid Request was received
	MOV	Temp, #00000011b	; Set EP0STALL and HSNAK
	JMP	SetEP0Control

NextDPTR:				; Returns (DPTR + byte DPTR is pointing to)
	MOVX	A, @DPTR
BumpDPTR:				; Returns (DPTR + ACC)
	ADD	A, DPL
	MOV	DPL, A
	JNC	Skip
	INC	DPH			; Need 16 bit arithmetic here
Skip:	RET

CorrectSubroutine:			; Jump to the subroutine that DPTR is pointing to
	MOV	DPTR, #CommandTable
	CALL	BumpDPTR		; Point to entry
	MOVX	A, @DPTR		; Get the offset
	MOV	DPTR, #CommandTable
	CALL	BumpDPTR                ; Get the routine address
	PUSH	DPL			; Create a RETURN address on stack
	PUSH	DPH                     ; Note: JMP @A+DPTR not used since A, DPTR needed
	MOV	R0, #ReplyBuffer+2
	CLR	A
	MOV	@R0, A			; Clear ReplyBuffer
	DEC	R0
	MOV	@R0, A
	DEC	R0
	MOV	@R0, #1			; Default non-descriptor reply
	MOV	DPTR, #SETUPDAT+2	; Point to LOW(wValue)
	MOVX	A, @DPTR		; Many of the routines need these
	MOV	B, A                    ; LOW(wValue) in B
	INC	DPTR
	MOVX	A, @DPTR            	; HIGH(wValue) in A
	CLR	STALL
	CLR	IsDescriptor
	RET				; Go to service routine

; Since the table only contains byte offsets, it is important that all these routines are
; within one page (100H) of CommandTable
;
CommandTable:
; First 16 commands are for the Device
	DB Device_Get_Status - CommandTable
	DB Device_Clear_Feature - CommandTable
	DB Invalid - CommandTable
	DB Device_Set_Feature - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable		; SIE implements Device_Set_Address
	DB Get_Descriptor - CommandTable
	DB Set_Descriptor - CommandTable
	DB Get_Configuration - CommandTable
	DB Set_Configuration - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
; Next 16 commands are for the Interface
	DB Interface_Get_Status - CommandTable
	DB Interface_Clear_Feature - CommandTable
	DB Invalid - CommandTable
	DB Interface_Set_Feature - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Get_Class_Descriptor - CommandTable
	DB Set_Class_Descriptor - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Get_Interface - CommandTable
	DB Set_Interface - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
; Next 16 commands are for the Endpoint
	DB Endpoint_Get_Status - CommandTable
	DB Endpoint_Clear_Feature - CommandTable
	DB Invalid - CommandTable
	DB Endpoint_Set_Feature - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Endpoint_Sync_Frame - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
; Next 16 commands are Class Requests
	DB Invalid - CommandTable
	DB Get_Report - CommandTable
	DB Get_Idle - CommandTable
	DB Get_Protocol - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Set_Report - CommandTable
	DB Set_Idle - CommandTable
	DB Set_Protocol - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
	DB Invalid - CommandTable
;
; Many requests are INVALID for this example
Get_Protocol:			; We are not a Boot device
Set_Protocol: 			; We are not a Boot device
Set_Descriptor:			; Our Descriptors are static
Set_Class_Descriptor:          	; Our Descriptors are static
Set_Interface:			; We only have one Interface
Get_Interface:			; We do not have an Alternate setting
Device_Set_Feature: 		; We have no features that can be set or cleared
Interface_Set_Feature: 		; We have no features that can be set or cleared
Endpoint_Set_Feature: 		; We have no features that can be set or cleared
Device_Clear_Feature:		; We have no features that can be set or cleared 
Interface_Clear_Feature: 	; We have no features that can be set or cleared
Endpoint_Sync_Frame:		; We are not an Isonchronous device

Invalid:			; Invalid Request made, STALL the Endpoint
	SETB	STALL
;
Endpoint_Clear_Feature: 	; We have no features that can be set or cleared
;
Reply:	RET

Set_Report:				; Host wants to sent us a Report. 
; The ONLY case in this example where host sends data to us
	JNB	Configured, Invalid	; Need to be Configured to do this command
	MOV	DPTR, #Out0ByteCount	; Enable EP0OutBuffer to receive data
	MOVX	@DPTR, A 		; Any value will do
        MOV	DPTR, #OUT07IRQ		; Wait for valid data in EP0OutBuffer
Wait4D:	MOVX	A, @DPTR
	ANL	A, #00000001b
	JZ	Wait4D
	MOVX	@DPTR, A		; Clear the interrupt
	JMP	ProcessOutputReport	; RETurn via this subroutine
Get_Report:				; Host wants a Report
	JNB	Configured, Invalid	; Need to be Configured to do this command 
	INC	R0			; Point to ReplyBuffer(1)
	MOV	@R0, #18H               ; Reply with a recognizable (arbitary) value
	RET
Set_Idle:				; Host wants to tell us how often we should talk
	JNB	Configured, Invalid	; Need to be Configured to do this command 
	MOV	Idle_Time, A
	RET				; Handshake with host
Get_Idle:				; Host must have forgotten what he told us to do
	JNB	Configured, Invalid	; Need to be Configured to do this command 
        INC	R0			; Point to ReplyBuffer(1)
	MOV	@R0, Idle_Time
	RET
Get_Configuration:
	JNB	Configured, Reply
					; If configured return a 1 (via Device_Get_Status)
Device_Get_Status:			; Only two bits of Device Status are defined
	INC	R0			; Point to ReplyBuffer(1)
	MOV	@R0, #1			; Bit 1=Remote Wakeup(=0), Bit 0=Self Powered(=1)
	RET

Interface_Get_Status:			; Interface Status is currently defined as 0
Endpoint_Get_Status:
        MOV	@R0, #2
	RET
Set_Configuration:   			; Valid values are 0 and 1
	MOV	A, B			; Get LOW(wValue)
	JZ	Deconfigured
	DEC	A
	JNZ	Invalid
	SETB	Configured
	RET
Deconfigured:
	CLR	Configured
	RET
Get_Descriptor:		   		; Host wants to know who/what we are
	SETB	IsDescriptor
	DEC	A			; Valid Values are 1, 2 and 3
	MOV	DPTR, #DeviceDescriptor
	JZ	Reply
	DEC	A
	MOV	DPTR, #ConfigurationDescriptor
	JZ	Reply
	DEC	A
	JNZ	Invalid
; Request is for a String Descriptor
	MOV	DPTR, #String0		; Point to String 0
	MOV	A, B			; Get String Index
NextString:
	JZ	FixUpthenReply
	MOV	Temp, A			; Save String Index
	CALL	NextDPTR
	MOVX	A, @DPTR		; Get the String Length (= 0 means we're at Backstop)
	JZ	Invalid			; Asked for a string I don't have 
	MOV	A, Temp
	DEC	A
	JMP	NextString		; Check if we are there yet
Get_Class_Descriptor: 			; Valid values are 21H, 22H, 23H for Class Request
	SETB	IsDescriptor
	CLR	C
	SUBB	A, #21H
	MOV	DPTR, #HIDDescriptor
	JZ	Reply
	DEC	A
	MOV	DPTR, #ReportDescriptor
	JZ	Reply
;	DEC	A			; This example does not use Physical Descriptors
;	JZ	Send_Physical_Descriptor
	JMP	Invalid
;
; Error check: this MUST be on within a page of CommandTable
WithinSamePage EQU $ - CommandTable	
;
FixUpthenReply:				; EZ-USB Rev D has a String Descriptor bug
					; Need to fill the IN0BUF (@ 7F00H) myself
	MOVX	A, @DPTR		; Get the string length
	MOV	R7, A			; Save counter
	MOV	B, A
	MOV	R0, #LOW(EP0InBuffer)	; PageReg = 7FH = HIGH(EP0InBuffer)
CopySD:	MOVX	@R0, A
	INC	R0
	INC	DPTR
        MOVX	A, @DPTR
	DJNZ	R7, CopySD
; Fixup complete, get back to the program flow
	POP	ACC			; Get rid of the return address
	POP	ACC
	MOV	A, B			; Retrieve byte count
	JMP	SendEP0InBuffer