	.module main

	;; define code segments link order
	.area CODE (CODE)
	.area CSEG (CODE)
	.area GSINIT (CODE)
	.area GSINIT2 (CODE)

	;; -----------------------------------------------------

	;; special function registers (which are not predefined)
	dpl0    = 0x82
	dph0    = 0x83
	dpl1    = 0x84
	dph1    = 0x85
	dps     = 0x86
	ckcon   = 0x8E
	spc_fnc = 0x8F
	exif    = 0x91
	mpage   = 0x92
	scon0   = 0x98
	sbuf0   = 0x99
	scon1   = 0xC0
	sbuf1   = 0xC1
	eicon   = 0xD8
	eie     = 0xE8
	eip     = 0xF8

	;; anchor xdata registers
	IN0BUF		= 0x7F00
	OUT0BUF		= 0x7EC0
	IN1BUF		= 0x7E80
	OUT1BUF		= 0x7E40
	IN2BUF		= 0x7E00
	OUT2BUF		= 0x7DC0
	IN3BUF		= 0x7D80
	OUT3BUF		= 0x7D40
	IN4BUF		= 0x7D00
	OUT4BUF		= 0x7CC0
	IN5BUF		= 0x7C80
	OUT5BUF		= 0x7C40
	IN6BUF		= 0x7C00
	OUT6BUF		= 0x7BC0
	IN7BUF		= 0x7B80
	OUT7BUF		= 0x7B40
	SETUPBUF	= 0x7FE8
	SETUPDAT	= 0x7FE8

	EP0CS		= 0x7FB4
	IN0BC		= 0x7FB5
	IN1CS		= 0x7FB6
	IN1BC		= 0x7FB7
	IN2CS		= 0x7FB8
	IN2BC		= 0x7FB9
	IN3CS		= 0x7FBA
	IN3BC		= 0x7FBB
	IN4CS		= 0x7FBC
	IN4BC		= 0x7FBD
	IN5CS		= 0x7FBE
	IN5BC		= 0x7FBF
	IN6CS		= 0x7FC0
	IN6BC		= 0x7FC1
	IN7CS		= 0x7FC2
	IN7BC		= 0x7FC3
	OUT0BC		= 0x7FC5
	OUT1CS		= 0x7FC6
	OUT1BC		= 0x7FC7
	OUT2CS		= 0x7FC8
	OUT2BC		= 0x7FC9
	OUT3CS		= 0x7FCA
	OUT3BC		= 0x7FCB
	OUT4CS		= 0x7FCC
	OUT4BC		= 0x7FCD
	OUT5CS		= 0x7FCE
	OUT5BC		= 0x7FCF
	OUT6CS		= 0x7FD0
	OUT6BC		= 0x7FD1
	OUT7CS		= 0x7FD2
	OUT7BC		= 0x7FD3

	IVEC		= 0x7FA8
	IN07IRQ		= 0x7FA9
	OUT07IRQ	= 0x7FAA
	USBIRQ		= 0x7FAB
	IN07IEN		= 0x7FAC
	OUT07IEN	= 0x7FAD
	USBIEN		= 0x7FAE
	USBBAV		= 0x7FAF
	BPADDRH		= 0x7FB2
	BPADDRL		= 0x7FB3

	SUDPTRH		= 0x7FD4
	SUDPTRL		= 0x7FD5
	USBCS		= 0x7FD6
	TOGCTL		= 0x7FD7
	USBFRAMEL	= 0x7FD8
	USBFRAMEH	= 0x7FD9
	FNADDR		= 0x7FDB
	USBPAIR		= 0x7FDD
	IN07VAL		= 0x7FDE
	OUT07VAL	= 0x7FDF
	AUTOPTRH	= 0x7FE3
	AUTOPTRL	= 0x7FE4
	AUTODATA	= 0x7FE5

	;; isochronous endpoints. only available if ISODISAB=0

	OUT8DATA	= 0x7F60
	OUT9DATA	= 0x7F61
	OUT10DATA	= 0x7F62
	OUT11DATA	= 0x7F63
	OUT12DATA	= 0x7F64
	OUT13DATA	= 0x7F65
	OUT14DATA	= 0x7F66
	OUT15DATA	= 0x7F67

	IN8DATA		= 0x7F68
	IN9DATA		= 0x7F69
	IN10DATA	= 0x7F6A
	IN11DATA	= 0x7F6B
	IN12DATA	= 0x7F6C
	IN13DATA	= 0x7F6D
	IN14DATA	= 0x7F6E
	IN15DATA	= 0x7F6F

	OUT8BCH		= 0x7F70
	OUT8BCL		= 0x7F71
	OUT9BCH		= 0x7F72
	OUT9BCL		= 0x7F73
	OUT10BCH	= 0x7F74
	OUT10BCL	= 0x7F75
	OUT11BCH	= 0x7F76
	OUT11BCL	= 0x7F77
	OUT12BCH	= 0x7F78
	OUT12BCL	= 0x7F79
	OUT13BCH	= 0x7F7A
	OUT13BCL	= 0x7F7B
	OUT14BCH	= 0x7F7C
	OUT14BCL	= 0x7F7D
	OUT15BCH	= 0x7F7E
	OUT15BCL	= 0x7F7F

	OUT8ADDR	= 0x7FF0
	OUT9ADDR	= 0x7FF1
	OUT10ADDR	= 0x7FF2
	OUT11ADDR	= 0x7FF3
	OUT12ADDR	= 0x7FF4
	OUT13ADDR	= 0x7FF5
	OUT14ADDR	= 0x7FF6
	OUT15ADDR	= 0x7FF7
	IN8ADDR		= 0x7FF8
	IN9ADDR		= 0x7FF9
	IN10ADDR	= 0x7FFA
	IN11ADDR	= 0x7FFB
	IN12ADDR	= 0x7FFC
	IN13ADDR	= 0x7FFD
	IN14ADDR	= 0x7FFE
	IN15ADDR	= 0x7FFF

	ISOERR		= 0x7FA0
	ISOCTL		= 0x7FA1
	ZBCOUNT		= 0x7FA2
	INISOVAL	= 0x7FE0
	OUTISOVAL	= 0x7FE1
	FASTXFR		= 0x7FE2

	;; CPU control registers

	CPUCS		= 0x7F92

	;; IO port control registers

	PORTACFG	= 0x7F93
	PORTBCFG	= 0x7F94
	PORTCCFG	= 0x7F95
	OUTA		= 0x7F96
	OUTB		= 0x7F97
	OUTC		= 0x7F98
	PINSA		= 0x7F99
	PINSB		= 0x7F9A
	PINSC		= 0x7F9B
	OEA		= 0x7F9C
	OEB		= 0x7F9D
	OEC		= 0x7F9E

	;; I2C controller registers

	I2CS		= 0x7FA5
	I2DAT		= 0x7FA6

	;; FPGA defines
	XC4K_IRLENGTH	= 3
	XC4K_EXTEST	= 0
	XC4K_PRELOAD	= 1
	XC4K_CONFIGURE	= 5
	XC4K_BYPASS	= 7

	FPGA_CONFIGSIZE = 11876
	FPGA_BOUND	= 344

	SOFTWARECONFIG	= 0

	;; -----------------------------------------------------

	.area CODE (CODE)
	ljmp	startup
	ljmp	int0_isr
	.ds	5
	ljmp	timer0_isr
	.ds	5
	ljmp	int1_isr
	.ds	5
	ljmp	timer1_isr
	.ds	5
	ljmp	ser0_isr
	.ds	5
	ljmp	timer2_isr
	.ds	5
	ljmp	resume_isr
	.ds	5
	ljmp	ser1_isr
	.ds	5
	ljmp	usb_isr
	.ds	5
	ljmp	i2c_isr
	.ds	5
	ljmp	int4_isr
	.ds	5
	ljmp	int5_isr
	.ds	5
	ljmp	int6_isr

	.ds	0x9a
	;; USB interrupt dispatch table
usb_isr:
	ljmp	usb_sudav_isr
	.ds	1
	ljmp	usb_sof_isr
	.ds	1
	ljmp	usb_sutok_isr
	.ds	1
	ljmp	usb_suspend_isr
	.ds	1
	ljmp	usb_usbreset_isr
	.ds	1
	reti
	.ds	3
	ljmp	usb_ep0in_isr
	.ds	1
	ljmp	usb_ep0out_isr
	.ds	1
	ljmp	usb_ep1in_isr
	.ds	1
	ljmp	usb_ep1out_isr
	.ds	1
	ljmp	usb_ep2in_isr
	.ds	1
	ljmp	usb_ep2out_isr
	.ds	1
	ljmp	usb_ep3in_isr
	.ds	1
	ljmp	usb_ep3out_isr
	.ds	1
	ljmp	usb_ep4in_isr
	.ds	1
	ljmp	usb_ep4out_isr
	.ds	1
	ljmp	usb_ep5in_isr
	.ds	1
	ljmp	usb_ep5out_isr
	.ds	1
	ljmp	usb_ep6in_isr
	.ds	1
	ljmp	usb_ep6out_isr
	.ds	1
	ljmp	usb_ep7in_isr
	.ds	1
	ljmp	usb_ep7out_isr

	;; -----------------------------------------------------

	.area   BSEG (BIT)

	.area	ISEG (DATA)
stack:		.ds	0x80

	.area	DSEG (DATA)
errcode:	.ds	1
errval:		.ds	1
cfgcount:	.ds	2
leddiv:		.ds	1
irqcount:	.ds	1
ctrlcode:	.ds	1
ctrladdr:	.ds	2
ctrllen:	.ds	2

	;; USB state
numconfig:	.ds	1
altsetting:	.ds	1
	
	.area	XSEG (DATA)


	.area	GSINIT (CODE)
startup:
	mov	sp,#stack	; -1
	clr	a
	mov	psw,a
	mov	dps,a
	;lcall	__sdcc_external_startup
	;mov	a,dpl0
	;jz	__sdcc_init_data
	;ljmp	__sdcc_program_startup
__sdcc_init_data:

	.area	GSINIT2 (CODE)
__sdcc_program_startup:
	;; assembler code startup
	clr	a
	mov	errcode,a
	mov	errval,a
	mov	cfgcount,a
	mov	cfgcount+1,a
 	mov	irqcount,a
 	mov	ctrlcode,a
	mov	dps,a
	mov	ie,a
	;; some indirect register setup
	mov	ckcon,#0x31	; one external wait state, to avoid chip bugs
	;; Timer setup:
	;; timer 0: LED blink    : T0M=0, CT0=0, MODE0=1
	;; timer 1: Baudrate gen : T1M=1, CT1=0, MODE1=2
	mov	tmod,#0x21
	mov	tcon,#0x55	; INT0/INT1 edge
	mov	th1,#256-156	; 1200 bauds
	mov	pcon,#0		; SMOD0=0
	;; give Windows a chance to finish the writecpucs control transfer
	;; 10ms delay loop
	mov	dptr,#(-6000)&0xffff
9$:	inc	dptr		; 3 cycles
	mov	a,dpl0		; 2 cycles
	orl	a,dph0		; 2 cycles
	jnz	9$		; 3 cycles
	;; init USB subsystem
	mov	dptr,#ISOCTL
	mov	a,#1		; disable ISO endpoints
	movx	@dptr,a
	mov	dptr,#USBBAV
	mov	a,#1		; enable autovector, disable breakpoint logic
	movx	@dptr,a
	clr	a
	mov	dptr,#INISOVAL
	movx	@dptr,a
	mov	dptr,#OUTISOVAL
	movx	@dptr,a
	mov	dptr,#USBPAIR
	mov	a,#0x9		; pair EP 2&3 for input & output
	movx	@dptr,a
	mov	dptr,#IN07VAL
	mov	a,#0x7		; enable EP0+EP1+EP2
	movx	@dptr,a
	mov	dptr,#OUT07VAL
	mov	a,#0x5		; enable EP0+EP2
	movx	@dptr,a
	;; USB:	init endpoint toggles
	mov	dptr,#TOGCTL
	mov	a,#0x12
	movx	@dptr,a
	mov	a,#0x32		; clear EP 2 in toggle
	movx	@dptr,a
	mov	a,#0x02
	movx	@dptr,a
	mov	a,#0x22		; clear EP 2 out toggle
	movx	@dptr,a
	;; configure IO ports
	mov	dptr,#PORTACFG
	mov	a,#0
	movx	@dptr,a
	mov	dptr,#OUTA
	mov	a,#0x80		; set PROG lo
	movx	@dptr,a
	mov	dptr,#OEA
	mov	a,#0xc2		; out: TMS,TDI,PROG  in: DONE
	movx	@dptr,a
	mov	dptr,#PORTBCFG
	mov	a,#0
	movx	@dptr,a
	mov	dptr,#OEB
	mov	a,#0
	movx	@dptr,a
	mov	dptr,#PORTCCFG
	mov	a,#0
	movx	@dptr,a
	mov	dptr,#OUTC
	mov	a,#0x20
	movx	@dptr,a
	mov	dptr,#OEC
	mov	a,#0x2e		; out: LEDCON,LEDSTA,TCK,INIT  in: TDO
	movx	@dptr,a
	;; enable interrupts
	mov	ie,#0x82	; enable timer 0
	mov	eie,#0x01	; enable USB interrupts
	mov	dptr,#USBIEN
	mov	a,#1		; enable SUDAV interrupt
	movx	@dptr,a
	mov	dptr,#IN07IEN
	mov	a,#3		; enable EP0+EP1 interrupt
	movx	@dptr,a
	mov	dptr,#OUT07IEN
	mov	a,#1		; enable EP0 interrupt
	movx	@dptr,a
	;; initialize UART 0 for config loading
	mov	scon0,#0x20	; mode 0, CLK24/4
	mov	sbuf0,#0xff
	;; initialize USB state
	clr	a
	mov	numconfig,a
	mov	altsetting,a
	.if	0
	;; disconnect from USB bus
	mov	dptr,#USBCS
	mov	a,#10
	movx	@dptr,a
	;; wait 0.3 sec
	mov	r2,#30
	;; 10ms delay loop
0$:	mov	dptr,#(-6000)&0xffff
1$:	inc	dptr            ; 3 cycles
	mov	a,dpl0          ; 2 cycles
	orl	a,dph0          ; 2 cycles
	jnz	1$              ; 3 cycles
	djnz	r2,0$
	;; reconnect to USB bus
	mov	dptr,#USBCS
	mov	a,#2		; 8051 handles control
	movx	@dptr,a
	mov	a,#6		; reconnect, 8051 handles control
	movx	@dptr,a
	.else
	mov	dptr,#USBCS
	mov	a,#6		; reconnect, 8051 handles control
	movx	@dptr,a
	.endif

	;; final
	lcall	fillusbintr
	;; kludge; first OUT2 packet seems to be bogus
	;; wait for packet with length 1 and contents 0x55

	;; some short delay
	mov	dptr,#OUTA
	mov	a,#0x82		; set PROG hi
	movx	@dptr,a
	mov	dptr,#PINSA
	movx	a,@dptr
	jnb	acc.2,8$
	;; DONE stuck high
	mov	errcode,#0x80
	ljmp	fpgaerr
8$:	lcall	jtag_reset_tap
	mov	r2,#5
	mov	r3,#0
	mov	r4,#6
	lcall	jtag_shiftout	; enter SHIFT-IR state
	mov	r2,#8
	mov	r3,#0
	mov	r4,#0
	lcall	jtag_shiftout	; assume max. 8bit IR
	mov	r2,#8
	mov	r3,#1
	mov	r4,#0
	lcall	jtag_shift	; shift in a single bit
	mov	a,#-(1<<XC4K_IRLENGTH)
	add	a,r7
	jz	2$
	;; JTAG instruction register error
	mov	errcode,#0x81
	mov	errval,r7
	ljmp	fpgaerr
2$:	mov	r2,#XC4K_IRLENGTH+2
	mov	r3,#XC4K_CONFIGURE
	mov	r4,#3<<(XC4K_IRLENGTH-1)
	lcall	jtag_shiftout	; shift in CONFIGURE insn, goto RUNTEST/IDLE state
	mov	dptr,#OEC
	mov	a,#0x2a		; out: LEDCON,LEDSTA,TCK  in: TDO,INIT
	movx	@dptr,a
	mov	errcode,#1
	mov	dptr,#PINSC
3$:	movx	a,@dptr
	jnb	acc.2,3$
	mov	r2,#3
	mov	r3,#0xff
	mov	r4,#1
	lcall	jtag_shiftout	; advance to SHIFT-DR state
	;; software configuration: enable UART special func pins
	mov	dptr,#OUTA
	mov	a,#0x42		; PROG hi, TMS lo, TDI hi
	movx	@dptr,a
	.if	SOFTWARECONFIG
	.else
	mov	dptr,#PORTACFG
	mov	a,#0x40		; TDI is special function pin
	movx	@dptr,a
	mov	dptr,#PORTCCFG
	mov	a,#0x02		; TCK is special function pin
	movx	@dptr,a
	.endif
	;; start
	mov	errcode,#0x10

	mov	dptr,#OUT2CS
	clr	a
	movx	@dptr,a
	mov	dptr,#OUT2BC
	movx	@dptr,a		; arm EP2 OUT
	
	
ldstloop:
	mov	dptr,#OUT2BC
	movx	@dptr,a		; arm EP2 OUT
0$:	mov	dptr,#OUT2CS
	movx	a,@dptr
	jb	acc.1,0$
	mov	dptr,#OUT2BC
	movx	a,@dptr
	add	a,#-2
	jnc	ldstloop
	mov	dptr,#OUT2BUF
	movx	a,@dptr
	cjne	a,#0xff,ldstloop
	inc	dptr
	movx	a,@dptr
	cjne	a,#0x04,ldstloop
	mov	errcode,#0x11
	sjmp	xxloadloop

loadloop:
	mov	dptr,#OUT2BC
	movx	@dptr,a		; arm EP2 OUT
0$:	mov	dptr,#OUT2CS
	movx	a,@dptr
	jb	acc.1,0$
xxloadloop:
	mov	dptr,#OUT2BC
	movx	a,@dptr
	jz	loadloop
	mov	r7,a
	add	a,cfgcount
	mov	cfgcount,a
	clr	a
	addc	a,cfgcount+1
	mov	cfgcount+1,a
	mov	dptr,#OUT2BUF
	.if	SOFTWARECONFIG
1$:	movx	a,@dptr
	mov	r3,a
	mov	r2,#8
	inc	dps
2$:	mov	a,#2
	xch	a,r3
	rrc	a
	xch	a,r3
	mov	acc.6,c
	mov	dptr,#OUTA
	movx	@dptr,a
	mov	dptr,#OUTC
	movx	a,@dptr
	setb	acc.1
	movx	@dptr,a
	clr	acc.1
	movx	@dptr,a
	djnz	r2,2$
	dec	dps
	inc	dptr
	djnz	r7,1$
	.else
	movx	a,@dptr
	sjmp	3$
1$:	movx	a,@dptr
2$:	jnb	scon0+1,2$
3$:	clr	scon0+1
	mov	sbuf0,a
	inc	dptr
	djnz	r7,1$
	.endif
	;; check if init went low - indicates configuration error
	mov	dptr,#PINSC
	movx	a,@dptr
	jb	acc.2,5$
	;; INIT low
	mov	dptr,#OUT2CS
	mov	a,#0x01
	movx	@dptr,a
	mov	errcode,#0x90
	ljmp	fpgaerr
5$:	;; check for end of FPGA configuration data
	mov	a,#<(-FPGA_CONFIGSIZE)
	add	a,cfgcount
	mov	a,#>(-FPGA_CONFIGSIZE)
	addc	a,cfgcount+1
	jnc	loadloop
	;; configuration download complete, start FPGA
	mov	dptr,#PORTBCFG
	mov	a,#0		; TDI no longer special function pin
	movx	@dptr,a
	mov	dptr,#PORTCCFG
	mov	a,#0		; TCK no longer special function pin
	movx	@dptr,a
	;; wait for done loop
	mov	r7,#250
doneloop:
	mov	dptr,#PINSA
	movx	a,@dptr
	jb	acc.2,doneactive
	mov	dptr,#PINSC
	movx	a,@dptr
	jb	acc.2,1$
	;; INIT low
	mov	errcode,#0x90
	ljmp	fpgaerr
1$:	mov	dptr,#OUTC
	movx	a,@dptr
	setb	acc.1
	movx	@dptr,a
	clr	acc.1
	movx	@dptr,a
	djnz	r7,doneloop
	;; DONE stuck low
	mov	errcode,#0x91
	ljmp	fpgaerr
	
doneactive:
	;; generate 16 clock pulses to start up FPGA
	mov	r7,#16
	mov	dptr,#OUTC
	movx	a,@dptr
0$:	setb	acc.1
	movx	@dptr,a
	clr	acc.1
	movx	@dptr,a
	djnz	r7,0$
	mov	dptr,#PORTCCFG
	mov	a,#0xc0		; RD/WR is special function pin
	movx	@dptr,a
	mov	errcode,#0x20

	mov	dptr,#0xc008
	mov	a,#0xc7
	movx	@dptr,a
	
fpgaerr:
	sjmp	fpgaerr

	.area	CSEG (CODE)
	ar2 = 0x02
	ar3 = 0x03
	ar4 = 0x04
	ar5 = 0x05
	ar6 = 0x06
	ar7 = 0x07
	ar0 = 0x00
	ar1 = 0x01

	;; jtag_shift
	;; r2 = num
	;; r3 = tdi
	;; r4 = tms
	;; return: r7 = tdo
jtag_reset_tap:
	mov	r2,#5
	mov	r3,#0
	mov	r4,#0xff
jtag_shiftout:
jtag_shift:
	mov	r6,#1
	mov	r7,#0
1$:	mov	a,#2
	xch	a,r3
	rrc	a
	xch	a,r3
	mov	acc.6,c
	xch	a,r4
	rrc	a
	xch	a,r4
	mov	acc.7,c
	mov	dptr,#OUTA
	movx	@dptr,a
	mov	dptr,#PINSC
	movx	a,@dptr
	jnb	acc.0,2$
	mov	a,r6
	orl	ar7,a
2$:	mov	dptr,#OUTC
	movx	a,@dptr
	setb	acc.1
	movx	@dptr,a
	clr	acc.1
	movx	@dptr,a
	mov	a,r6
	rl	a
	mov	r6,a
	djnz	r2,1$
	ret

	;; EEPROM address where the serial number is located
	
eepromstraddr:
	.db	0x10

	;; I2C Routines
	;; note: ckcon should be set to #0x31 to avoid chip bugs

writei2c:
	;; dptr: data to be sent
	;; b:    device address
	;; r7:   transfer length
	;; return: a: status (bytes remaining, 0xff bus error)
	inc	dps
	mov	dptr,#I2CS
	clr	b.0		; r/w = 0
	mov	a,#0x80		; start condition
	movx	@dptr,a
	mov	dptr,#I2DAT
	mov	a,b
	movx	@dptr,a
	mov	dptr,#I2CS	
0$:	movx	a,@dptr
	jb	acc.2,3$	; BERR
	jnb	acc.0,0$	; DONE
	jnb	acc.1,1$	; ACK
	mov	dptr,#I2DAT
	dec	dps
	movx	a,@dptr
	inc	dptr
	inc	dps
	movx	@dptr,a
	mov	dptr,#I2CS
	djnz	r7,0$
1$:	mov	a,#0x40		; stop condition
	movx	@dptr,a
2$:	movx	a,@dptr
	jb	acc.6,2$
	dec	dps
	mov	a,r7
	ret
3$:	mov	r7,#255
	sjmp	1$

readi2c:
	;; dptr: data to be sent
	;; b:    device address
	;; r7:   transfer length
	;; return: a: status (bytes remaining, 0xff bus error)
	inc	dps
	mov	dptr,#I2CS
	setb	b.0		; r/w = 1
	mov	a,#0x80		; start condition
	movx	@dptr,a
	mov	dptr,#I2DAT
	mov	a,b
	movx	@dptr,a
	mov	dptr,#I2CS
0$:	movx	a,@dptr
	jb	acc.2,9$	; BERR
	jnb	acc.0,0$	; DONE
	jnb	acc.1,5$	; ACK
	mov	a,r7
	jnz	1$
5$:	mov	a,#0x40		; stop condition
	movx	@dptr,a
2$:	movx	a,@dptr
	jb	acc.6,2$
	dec	dps
	clr	a
	ret
1$:	cjne	a,#1,3$
	mov	a,#0x20		; LASTRD = 1
	movx	@dptr,a
3$:	mov	dptr,#I2DAT
	movx	a,@dptr		; initiate first read
	mov	dptr,#I2CS
4$:	movx	a,@dptr
	jb	acc.2,9$	; BERR
	jnb	acc.0,4$	; DONE
	mov	a,r7
	cjne	a,#2,6$
	mov	a,#0x20		; LASTRD = 1
	movx	@dptr,a
6$:	cjne	a,#1,7$
	mov	a,#0x40		; stop condition
	movx	@dptr,a
7$:	mov	dptr,#I2DAT
	movx	a,@dptr		; read data
	dec	dps
	movx	@dptr,a
	inc	dptr
	inc	dps
	mov	dptr,#I2CS
	djnz	r7,4$
8$:	movx	a,@dptr
	jb	acc.6,8$
	dec	dps
	clr	a
	ret
9$:	mov	a,#0x40		; stop condition
	movx	@dptr,a
10$:	movx	a,@dptr
	jb	acc.6,10$
	mov	a,#255
	dec	dps
	ret

	;; ------------------ interrupt handlers ------------------------

int0_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	tcon+1
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

timer0_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	tcon+5
	;; handle interrupt
	inc	leddiv
	mov	a,leddiv
	anl	a,#7
	jnz	0$
	mov	dptr,#OUTC
	movx	a,@dptr
	xrl	a,#0x08
	movx	@dptr,a
0$:
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

int1_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	tcon+3
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

timer1_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	tcon+7
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

ser0_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	scon0+0
	clr	scon0+1
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

timer2_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	t2con+7
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

resume_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	eicon+4
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

ser1_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	scon1+0
	clr	scon1+1
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

i2c_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.5
	mov	exif,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

int4_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.6
	mov	exif,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

int5_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.7
	mov	exif,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

int6_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	clr	eicon+3
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

xmemread::
	mov	a,ctrllen
	mov	r7,a
	add	a,#-64
	mov	ctrllen,a
	mov	a,ctrllen+1
	addc	a,#0
	jnc	0$
	clr	a
	mov	ctrllen,a
	mov	ctrllen+1,a
	mov	ctrlcode,a
	mov	a,#2		; ack control transfer
	mov	dptr,#EP0CS
	movx	@dptr,a
	mov	a,r7
	jz	2$
	sjmp	1$
0$:	mov	ctrllen+1,a
	mov	r7,#64
1$:	mov	ar0,r7
	mov	dpl0,ctrladdr
	mov	dph0,ctrladdr+1
	inc	dps
	mov	dptr,#IN0BUF
	dec	dps
3$:	movx	a,@dptr
	inc	dptr
	inc	dps
	movx	@dptr,a
	inc	dptr
	dec	dps
	djnz	r0,3$
	mov	ctrladdr,dpl0
	mov	ctrladdr+1,dph0
2$:	mov	a,r7
	mov	dptr,#IN0BC
	movx	@dptr,a
	ret

usb_sudav_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	dpl1
	push	dph1
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	push	ar0
	push	ar7
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#USBIRQ
	mov	a,#0x01
	movx	@dptr,a
	;; handle interrupt
	mov	ctrlcode,#0		; reset control out code
	mov	dptr,#SETUPDAT+1
	movx	a,@dptr			; bRequest field
	;; standard commands
	;; USB_REQ_GET_DESCRIPTOR
	cjne	a,#USB_REQ_GET_DESCRIPTOR,cmdnotgetdesc
	mov	dptr,#SETUPDAT		; bRequestType == 0x80
	movx	a,@dptr
	cjne	a,#USB_DIR_IN,setupstallstd
	mov	dptr,#SETUPDAT+3
	movx	a,@dptr
	cjne	a,#USB_DT_DEVICE,cmdnotgetdescdev
	mov	dptr,#SUDPTRH
	mov	a,#>devicedescr
	movx	@dptr,a
	inc	dptr
	mov	a,#<devicedescr
	movx	@dptr,a
	sjmp	setupackstd
cmdnotgetdescdev:
	cjne	a,#USB_DT_CONFIG,cmdnotgetdesccfg
	mov	dptr,#SETUPDAT+2
	movx	a,@dptr
	jnz	setupstallstd
	mov	dptr,#SUDPTRH
	mov	a,#>config0descr
	movx	@dptr,a
	inc	dptr
	mov	a,#<config0descr
	movx	@dptr,a
	sjmp	setupackstd
cmdnotgetdesccfg:
	cjne	a,#USB_DT_STRING,setupstallstd
	mov	dptr,#SETUPDAT+2
	movx	a,@dptr
	add	a,#-numstrings
	jc	setupstallstd
	movx	a,@dptr
	add	a,acc
	add	a,#<stringdescr
	mov	dpl0,a
	clr	a
	addc	a,#>stringdescr
	mov	dph0,a
	movx	a,@dptr
	mov	b,a
	inc	dptr
	movx	a,@dptr
	mov	dptr,#SUDPTRH
	movx	@dptr,a
	inc	dptr
	mov	a,b
	movx	@dptr,a
	; sjmp	setupackstd	
setupackstd:
	ljmp	setupack
setupstallstd:
	ljmp	setupstall
cmdnotgetdesc:
	;; USB_REQ_SET_CONFIGURATION
	cjne	a,#USB_REQ_SET_CONFIGURATION,cmdnotsetconf
	mov	dptr,#SETUPDAT
	movx	a,@dptr
	jnz	setupstallstd
	mov	dptr,#SETUPDAT+2
	movx	a,@dptr
	add	a,#-2
	jc	setupstallstd
	movx	a,@dptr
	mov	numconfig,a
cmdresettoggleshalt:
	mov	dptr,#TOGCTL
	mov	r0,#7
0$:	mov	a,r0
	orl	a,#0x10
	movx	@dptr,a
	orl	a,#0x30
	movx	@dptr,a
	mov	a,r0
	movx	@dptr,a
	orl	a,#0x20
	movx	@dptr,a
	djnz	r0,0$
	clr	a
	movx	@dptr,a
	mov	a,#2
	mov	dptr,#IN1CS
	mov	r0,#7
1$:	movx	@dptr,a
	inc	dptr
	inc	dptr
	djnz	r0,1$
	mov	dptr,#OUT1CS
	mov	r0,#7
2$:	movx	@dptr,a
	inc	dptr
	inc	dptr
	djnz	r0,2$
	lcall	fillusbintr
	sjmp	setupackstd
cmdnotsetconf:
	;; USB_REQ_SET_INTERFACE
	cjne	a,#USB_REQ_SET_INTERFACE,cmdnotsetint
	mov	dptr,#SETUPDAT
	movx	a,@dptr
	cjne	a,#USB_RECIP_INTERFACE|USB_DIR_OUT,setupstallstd
	mov	a,numconfig
	cjne	a,#1,setupstallstd
	mov	dptr,#SETUPDAT+4
	movx	a,@dptr
	jnz	setupstallstd
	mov	dptr,#SETUPDAT+2
	movx	a,@dptr
	mov	altsetting,a
	sjmp	cmdresettoggleshalt
cmdnotsetint:
	;; USB_REQ_GET_INTERFACE
	cjne	a,#USB_REQ_GET_INTERFACE,cmdnotgetint
	mov	dptr,#SETUPDAT
	movx	a,@dptr
	cjne	a,#USB_RECIP_INTERFACE|USB_DIR_IN,setupstallstd
	mov	a,numconfig
	cjne	a,#1,setupstallstd
	mov	dptr,#SETUPDAT+4
	movx	a,@dptr
	jnz	setupstallstd
	mov	a,altsetting
cmdrespondonebyte:
	mov	dptr,#IN0BUF
	movx	@dptr,a
	mov	dptr,#IN0BC
	mov	a,#1
	movx	@dptr,a	
	sjmp	setupackstd2
cmdnotgetint:
	;; USB_REQ_GET_CONFIGURATION
	cjne	a,#USB_REQ_GET_CONFIGURATION,cmdnotgetconf
	mov	dptr,#SETUPDAT
	movx	a,@dptr
	cjne	a,#USB_RECIP_DEVICE|USB_DIR_IN,setupstallstd2
	mov	a,numconfig
	sjmp	cmdrespondonebyte	
cmdnotgetconf:
	;; USB_REQ_GET_STATUS (0)
	jnz	cmdnotgetstat
	mov	dptr,#SETUPDAT
	movx	a,@dptr
	cjne	a,#USB_RECIP_DEVICE|USB_DIR_IN,cmdnotgetstatdev
	mov	a,#1
cmdrespondstat:
	mov	dptr,#IN0BUF
	movx	@dptr,a
	inc	dptr
	clr	a
	movx	@dptr,a
	mov	dptr,#IN0BC
	mov	a,#2
	movx	@dptr,a	
	sjmp	setupackstd2
cmdnotgetstatdev:
	cjne	a,#USB_RECIP_INTERFACE|USB_DIR_IN,cmdnotgetstatintf
	clr	a
	sjmp	cmdrespondstat
cmdnotgetstatintf:
	cjne	a,#USB_RECIP_ENDPOINT|USB_DIR_IN,setupstallstd2
	mov	dptr,#SETUPDAT+4
	movx	a,@dptr
	mov	dptr,#OUT1CS-2
	jnb	acc.7,0$
	mov	dptr,#IN1CS-2
0$:	anl	a,#15
	jz	setupstallstd2
	jb	acc.3,setupstallstd2
	add	a,acc
	add	a,dpl0
	mov	dpl0,a
	movx	a,@dptr
	sjmp	cmdrespondstat
setupackstd2:
	ljmp	setupack
setupstallstd2:
	ljmp	setupstall
cmdnotgetstat:
	;; USB_REQ_SET_FEATURE
	cjne	a,#USB_REQ_SET_FEATURE,cmdnotsetftr
	mov	b,#1
	sjmp	handleftr
cmdnotsetftr:
	;; USB_REQ_CLEAR_FEATURE
	cjne	a,#USB_REQ_CLEAR_FEATURE,cmdnotclrftr
	mov	b,#0
handleftr:
	mov	dptr,#SETUPDAT
	movx	a,@dptr
	cjne	a,#USB_RECIP_ENDPOINT|USB_DIR_OUT,setupstallstd2
	inc	dptr
	inc	dptr
	movx	a,@dptr
	jnz	setupstallstd2	; not ENDPOINT_HALT feature
	inc	dptr
	movx	a,@dptr
	jnz	setupstallstd2
	inc	dptr
	movx	a,@dptr
	mov	dptr,#OUT1CS-2
	jnb	acc.7,0$
	mov	dptr,#IN1CS-2
	orl	a,#0x10
0$:	jb	acc.3,setupstallstd2
	;; clear data toggle
	anl	a,#0x1f
	inc	dps
	mov	dptr,#TOGCTL
	movx	@dptr,a
	orl	a,#0x20
	movx	@dptr,a
	anl	a,#15
	movx	@dptr,a
	dec	dps	
	;; clear/set ep halt feature
	add	a,acc
	add	a,dpl0
	mov	dpl0,a
	mov	a,b
	movx	@dptr,a
	sjmp	setupackstd2
cmdnotclrftr:
	;; vendor specific commands
	;; 0xa3
	cjne	a,#0xa3,cmdnota3
	mov	dptr,#SETUPDAT+2
	movx	a,@dptr
	mov	ctrladdr,a
	inc	dptr
	movx	a,@dptr
	mov	ctrladdr+1,a
	add	a,#80
	jnc	setupstalla3
	mov	dptr,#SETUPDAT+6
	movx	a,@dptr
	mov	ctrllen,a
	add	a,ctrladdr
	inc	dptr
	movx	a,@dptr
	mov	ctrllen+1,a
	addc	a,ctrladdr+1
	jc	setupstalla3
	mov	dptr,#SETUPDAT		; bRequestType == 0x40
	movx	a,@dptr
	cjne	a,#0x40,1$
	mov	ctrlcode,#1
	mov	dptr,#OUT0BC
	movx	@dptr,a
	ljmp	endusbisr
1$:	cjne	a,#0xc0,setupstalla3	; bRequestType == 0xc0
	mov	ctrlcode,#2
	lcall	xmemread
	ljmp	endusbisr
setupstalla3:
	ljmp	setupstall
cmdnota3:
	;; 0xb1
	cjne	a,#0xb1,cmdnotb1
	mov	dptr,#SETUPDAT		; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstallb1
	mov	dptr,#IN0BUF
	mov	a,errcode
	movx	@dptr,a
	inc	dptr
	mov	a,errval
	movx	@dptr,a
	inc	dptr
	mov	a,sp

;;; xxxx
	push	dpl0
	push	dph0
	mov	dptr,#OUT07VAL
	movx	a,@dptr
	pop	dph0
	pop	dpl0
;;; xxxx
	
	movx	@dptr,a
	inc	dptr
	mov	a,#0

;;; xxxx
	push	dpl0
	push	dph0
	mov	dptr,#OUT2CS
	movx	a,@dptr
	pop	dph0
	pop	dpl0
;;; xxxx
	
	movx	@dptr,a
	inc	dptr
	mov	dptr,#IN0BC
	mov	a,#4
	movx	@dptr,a
	ljmp	setupack
setupstallb1:
	ljmp	setupstall
cmdnotb1:
	;; 0xb2
	cjne	a,#0xb2,cmdnotb2
	mov	dptr,#SETUPDAT		; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstallb2
	mov	dptr,#IN0BUF
	mov	a,errcode
	movx	@dptr,a
	inc	dptr
	mov	a,errval
	movx	@dptr,a
	inc	dptr
	mov	a,cfgcount
	movx	@dptr,a
	inc	dptr
	mov	a,cfgcount+1
	movx	@dptr,a
	inc	dptr
	mov	a,irqcount
	movx	@dptr,a
	mov	dptr,#IN0BC
	mov	a,#5
	movx	@dptr,a
	inc	irqcount
	ljmp	setupack
setupstallb2:
	ljmp	setupstall
cmdnotb2:
	;; 0xb3
	cjne	a,#0xb3,cmdnotb3
	mov	dptr,#SETUPDAT		; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstallb3
	;; read EEPROM
	serstrlen = 6
	mov	b,#0xa0		; EEPROM address
	mov	dptr,#eepromstraddr
	mov	r7,#1
	lcall	writei2c
	jnz	setupstallb3
	mov	dptr,#IN0BUF
	mov	r7,#serstrlen
	lcall	readi2c
	jnz	setupstallb3
	mov	a,#serstrlen
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
setupstallb3:
	ljmp	setupstall
cmdnotb3:
	;; 0xb4
	cjne	a,#0xb4,cmdnotb4
	mov	dptr,#SETUPDAT+7
	movx	a,@dptr
	jnz	setupstallb4
	mov	dptr,#SETUPDAT+6
	movx	a,@dptr
	mov	r0,a
	mov	r7,a
	add	a,#-64
	jc	setupstallb4
	mov	dptr,#SETUPDAT+4	; wIndex
	movx	a,@dptr
	mov	b,a
	mov	ctrladdr,a
	mov	dptr,#SETUPDAT
	movx	a,@dptr
	cjne	a,#0x40,0$		; bRequestType == 0x40
	mov	ctrlcode,#2
	mov	dptr,#OUT0BC
	movx	@dptr,a
	ljmp	endusbisr
0$:	cjne	a,#0xc0,setupstallb4	; bRequestType == 0xc0
	mov	dptr,#IN0BUF
	lcall	readi2c
	jnz	setupstallb4
	mov	a,r0
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
setupstallb4:
	ljmp	setupstall
cmdnotb4:
	;; unknown commands fall through to setupstall

setupstall:
	mov	a,#3
	sjmp	endsetup
setupack:
	mov	a,#2
endsetup:
	mov	dptr,#EP0CS
	movx	@dptr,a
endusbisr:
	;; epilogue
	pop	ar7
	pop	ar0
	pop	dps
	pop	psw
	pop	dph1
	pop	dpl1
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_sof_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#USBIRQ
	mov	a,#0x02
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti


usb_sutok_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#USBIRQ
	mov	a,#0x04
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_suspend_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#USBIRQ
	mov	a,#0x08
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_usbreset_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#USBIRQ
	mov	a,#0x10
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep0in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	dpl1
	push	dph1
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	push	ar0
	push	ar7
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x01
	movx	@dptr,a
	;; handle interrupt
	mov	a,ctrlcode
	cjne	a,#2,0$
	lcall	xmemread
	sjmp	ep0inendisr
0$:	mov	dptr,#IN0BC
	clr	a
	movx	@dptr,a
	sjmp	ep0inack

ep0install:
	mov	a,#3
	sjmp	ep0incs
ep0inack:
	mov	a,#2
ep0incs:
	mov	dptr,#EP0CS
	movx	@dptr,a
ep0inendisr:
	;; epilogue
	pop	ar7
	pop	ar0
	pop	dps
	pop	psw
	pop	dph1
	pop	dpl1
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep0out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	dpl1
	push	dph1
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	push	ar0
	push	ar6
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x01
	movx	@dptr,a
	;; handle interrupt
	mov	a,ctrlcode		; check control code
	cjne	a,#0x01,i2cwr
	;; write to external memory
	mov	dptr,#OUT0BC
	movx	a,@dptr
	jz	0$
	mov	r7,a
	clr	c
	mov	a,ctrllen
	subb	a,r7
	mov	ctrllen,a
	mov	a,ctrllen+1
	subb	a,#0
	mov	ctrllen+1,a
	jc	ep0outstall
	mov	dptr,#OUT0BUF
	mov	dpl1,ctrladdr
	mov	dph1,ctrladdr+1
1$:	movx	a,@dptr
	inc	dptr
	inc	dps
	movx	@dptr,a
	inc	dptr
	dec	dps
	djnz	r7,1$
	mov	ctrladdr,dpl1
	mov	ctrladdr+1,dph1
0$:	mov	a,ctrllen
	orl	a,ctrllen+1
	jz	ep0outack
	sjmp	ep0outendisr

	;; write I2C eeprom
i2cwr:	cjne	a,#0x02,ep0outstall
	mov	dptr,#OUT0BC
	movx	a,@dptr
	jz	ep0outack
	mov	r7,a
	mov	b,ctrladdr
	mov	dptr,#OUT0BUF
	lcall	writei2c
	jnz	ep0outstall
	sjmp	ep0outack

ep0outstall:
	mov	ctrlcode,#0
	mov	a,#3
	sjmp	ep0outcs
ep0outack:
	mov	ctrlcode,#0
	mov	a,#2
ep0outcs:
	mov	dptr,#EP0CS
	movx	@dptr,a
ep0outendisr:
	;; epilogue
	pop	ar6
	pop	ar0
	pop	dps
	pop	psw
	pop	dph1
	pop	dpl1
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

fillusbintr::
	mov	dptr,#IN1BUF
	mov	a,errcode
	movx	@dptr,a
	inc	dptr
	mov	a,errval
	movx	@dptr,a
	inc	dptr
	mov	a,cfgcount
	movx	@dptr,a
	inc	dptr
	mov	a,cfgcount+1
	movx	@dptr,a
	inc	dptr
	mov	a,irqcount
	movx	@dptr,a
	mov	dptr,#IN1BC
	mov	a,#5
	movx	@dptr,a
	inc	irqcount
	ret

usb_ep1in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x02
	movx	@dptr,a
	;; handle interrupt
	lcall	fillusbintr
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep1out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x02
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep2in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x04
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep2out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x04
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep3in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x08
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep3out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x08
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep4in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x10
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep4out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x10
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep5in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x20
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep5out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x20
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep6in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x40
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep6out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x40
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep7in_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#IN07IRQ
	mov	a,#0x80
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

usb_ep7out_isr:
	push	acc
	push	b
	push	dpl0
	push	dph0
	push	psw
	mov	psw,#0x00
	push	dps
	mov	dps,#0
	;; clear interrupt
	mov	a,exif
	clr	acc.4
	mov	exif,a
	mov	dptr,#OUT07IRQ
	mov	a,#0x80
	movx	@dptr,a
	;; handle interrupt
	;; epilogue
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti
	
	;; -----------------------------------------------------
	;; USB descriptors
	;; -----------------------------------------------------

	;; Device and/or Interface Class codes
	USB_CLASS_PER_INTERFACE         = 0
	USB_CLASS_AUDIO                 = 1
	USB_CLASS_COMM                  = 2
	USB_CLASS_HID                   = 3
	USB_CLASS_PRINTER               = 7
	USB_CLASS_MASS_STORAGE          = 8
	USB_CLASS_HUB                   = 9
	USB_CLASS_VENDOR_SPEC           = 0xff

	;; Descriptor types
	USB_DT_DEVICE                   = 0x01
	USB_DT_CONFIG                   = 0x02
	USB_DT_STRING                   = 0x03
	USB_DT_INTERFACE                = 0x04
	USB_DT_ENDPOINT                 = 0x05

	;; Standard requests
	USB_REQ_GET_STATUS              = 0x00
	USB_REQ_CLEAR_FEATURE           = 0x01
	USB_REQ_SET_FEATURE             = 0x03
	USB_REQ_SET_ADDRESS             = 0x05
	USB_REQ_GET_DESCRIPTOR          = 0x06
	USB_REQ_SET_DESCRIPTOR          = 0x07
	USB_REQ_GET_CONFIGURATION       = 0x08
	USB_REQ_SET_CONFIGURATION       = 0x09
	USB_REQ_GET_INTERFACE           = 0x0A
	USB_REQ_SET_INTERFACE           = 0x0B
	USB_REQ_SYNCH_FRAME             = 0x0C

	;; USB Request Type and Endpoint Directions
	USB_DIR_OUT                     = 0
	USB_DIR_IN                      = 0x80

	USB_TYPE_STANDARD               = (0x00 << 5)
	USB_TYPE_CLASS                  = (0x01 << 5)
	USB_TYPE_VENDOR                 = (0x02 << 5)
	USB_TYPE_RESERVED               = (0x03 << 5)

	USB_RECIP_DEVICE                = 0x00
	USB_RECIP_INTERFACE             = 0x01
	USB_RECIP_ENDPOINT              = 0x02
	USB_RECIP_OTHER                 = 0x03

	;; Request target types.
	USB_RT_DEVICE                   = 0x00
	USB_RT_INTERFACE                = 0x01
	USB_RT_ENDPOINT                 = 0x02

	VENDID	= 0xbac0
	PRODID	= 0x6135

devicedescr:
	.db	18			; bLength
	.db	USB_DT_DEVICE		; bDescriptorType
	.db	0x00, 0x01		; bcdUSB
	.db	USB_CLASS_VENDOR_SPEC	; bDeviceClass
	.db	0			; bDeviceSubClass
	.db	0xff			; bDeviceProtocol
	.db	0x40			; bMaxPacketSize0
	.db	<VENDID,>VENDID		; idVendor
	.db	<PRODID,>PRODID		; idProduct
	.db	0x02,0x00		; bcdDevice
	.db	1			; iManufacturer
	.db	2			; iProduct
	.db	3			; iSerialNumber
	.db	1			; bNumConfigurations

config0descr:
	.db	9			; bLength
	.db	USB_DT_CONFIG		; bDescriptorType
	.db	<config0sz,>config0sz	; wTotalLength
	.db	1			; bNumInterfaces
	.db	1			; bConfigurationValue
	.db	0			; iConfiguration
	.db	0b01000000		; bmAttributs (self powered)
	.db	0			; MaxPower (mA/2) (self powered so 0)
	;; interface descriptor I0:A0
	.db	9			; bLength
	.db	USB_DT_INTERFACE	; bDescriptorType
	.db	0			; bInterfaceNumber
	.db	0			; bAlternateSetting
	.db	3			; bNumEndpoints
	.db	0xff			; bInterfaceClass (vendor specific)
	.db	0x00			; bInterfaceSubClass
	.db	0xff			; bInterfaceProtocol (vendor specific)
	.db	0			; iInterface
	;; endpoint descriptor I0:A0:E0
	.db	7			; bLength
	.db	USB_DT_ENDPOINT		; bDescriptorType
	.db	(USB_DIR_IN | 1)	; bEndpointAddress
	.db	0x02			; bmAttributes (bulk)
	.db	0x40,0x00		; wMaxPacketSize
	.db	0			; bInterval
	;; endpoint descriptor I0:A0:E1
	.db	7			; bLength
	.db	USB_DT_ENDPOINT		; bDescriptorType
	.db	(USB_DIR_IN | 2)	; bEndpointAddress
	.db	0x02			; bmAttributes (bulk)
	.db	0x40,0x00		; wMaxPacketSize
	.db	0			; bInterval
	;; endpoint descriptor I0:A0:E2
	.db	7			; bLength
	.db	USB_DT_ENDPOINT		; bDescriptorType
	.db	(USB_DIR_OUT | 2)	; bEndpointAddress
	.db	0x02			; bmAttributes (bulk)
	.db	0x40,0x00		; wMaxPacketSize
	.db	0			; bInterval
	;; interface descriptor I0:A1
	.db	9			; bLength
	.db	USB_DT_INTERFACE	; bDescriptorType
	.db	0			; bInterfaceNumber
	.db	1			; bAlternateSetting
	.db	3			; bNumEndpoints
	.db	0xff			; bInterfaceClass (vendor specific)
	.db	0x00			; bInterfaceSubClass
	.db	0xff			; bInterfaceProtocol (vendor specific)
	.db	0			; iInterface
	;; endpoint descriptor I0:A1:E0
	.db	7			; bLength
	.db	USB_DT_ENDPOINT		; bDescriptorType
	.db	(USB_DIR_IN | 1)	; bEndpointAddress
	.db	0x03			; bmAttributes (interrupt)
	.db	0x40,0x00		; wMaxPacketSize
	.db	10			; bInterval
	;; endpoint descriptor I0:A1:E1
	.db	7			; bLength
	.db	USB_DT_ENDPOINT		; bDescriptorType
	.db	(USB_DIR_IN | 2)	; bEndpointAddress
	.db	0x02			; bmAttributes (bulk)
	.db	0x40,0x00		; wMaxPacketSize
	.db	0			; bInterval
	;; endpoint descriptor I0:A1:E2
	.db	7			; bLength
	.db	USB_DT_ENDPOINT		; bDescriptorType
	.db	(USB_DIR_OUT | 2)	; bEndpointAddress
	.db	0x02			; bmAttributes (bulk)
	.db	0x40,0x00		; wMaxPacketSize
	.db	0			; bInterval

config0sz = . - config0descr

stringdescr:
	.db	<string0,>string0
	.db	<string1,>string1
	.db	<string2,>string2
	.db	<stringserial,>stringserial

numstrings = (. - stringdescr)/2

string0:
	.db	string0sz		; bLength
	.db	USB_DT_STRING		; bDescriptorType
	.db	0,0			; LANGID[0]: Lang Neutral
string0sz = . - string0

string1:
	.db	string1sz		; bLength
	.db	USB_DT_STRING		; bDescriptorType
	.db	'B,0,'a,0,'y,0,'c,0,'o,0,'m,0
string1sz = . - string1

string2:
	.db	string2sz		; bLength
	.db	USB_DT_STRING		; bDescriptorType
	.db	'U,0,'S,0,'B,0,'F,0,'L,0,'E,0,'X,0,' ,0
	.db	'(,0,'b,0,'l,0,'a,0,'n,0,'k,0,'),0
string2sz = . - string2

stringserial:
	.db	2			; bLength
	.db	USB_DT_STRING		; bDescriptorType
	.dw	0,0,0,0,0,0,0,0
	.dw	0,0,0,0,0,0,0,0

