	.module main

	;; ENDPOINTS
	;; EP0 in/out   Control
	;; EP1 in       Interrupt:  Status
	;;              Byte 0: Modem Status
	;;                Bit 0-1: Transmitter status
	;;                         0: idle (off)
	;;                         1: keyup
	;;                         2: transmitting packets
	;;                         3: tail
	;;                Bit 2:   PTT status (1=on)
	;;                Bit 3:   DCD
	;;                Bit 4:   RXB (Rx Packet Ready)
	;;                Bit 5:   UART transmitter empty
	;;                Bit 6-7: unused
	;;              Byte 1: Number of empty 64 byte chunks in TX fifo
	;;              Byte 2: Number of full 64 byte chunks in RX fifo
	;;              Byte 3: RSSI value
	;;              Byte 4:	IRQ count
	;;              Byte 5-20: (as needed) UART receiver chars
	;; EP2 out      Packets to be transmitted
	;; EP2 in       Received packets; note they have the CRC appended

	;; COMMAND LIST
	;; C0 C0  read status (max. 23 bytes, first 6 same as EP1 in)
	;; C0 C8  read mode
	;;          Return:
	;;            Byte 0: 1 (MODE_FSK) or 2 (MODE_EXTERNAL)
	;; C0 C9  return serial number string
	;; C0 D0  get/set PTT/DCD/RSSI
	;;          wIndex = 1:	 set forced ptt to wValue
	;;          Return:
	;;            Byte 0: PTT status
	;;            Byte 1: DCD status
	;;            Byte 2: RSSI status
	;; C0 D1  get Bitrate
	;;          Return:
	;;            Byte 0-2: TX Bitrate
	;;            Byte 3-5: RX Bitrate
	;; 40 D2  set CON/STA led
	;;          Bits 0-1 of wValue
	;; 40 D3  send byte to UART
	;;          Byte in wValue
	;; C0 D4  get/set modem disconnect port (only if internal modem used, stalls otherwise)
	;;          wIndex = 1:	 write wValue to output register
	;;          wIndex = 2:	 write wValue to tristate mask register (1 = input, 0 = output)
	;;          Return:
	;;            Byte 0: Modem Disconnect Input
	;;            Byte 1: Modem Disconnect Output register
	;;            Byte 2: Modem Disconnect Tristate register
	;; C0 D5  get/set T7F port
	;;          wIndex = 1:	 write wValue to T7F output register
	;;          Return:
	;;            Byte 0: T7F Input
	;;            Byte 1: T7F Output register

	;; 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

	;; Xilinx FPGA registers
	FSKRXCNT	= 0xc001
	FSKRXDATA	= 0xc002
	FSKRXFLAG	= 0xc003
	FSKTXCNT	= 0xc000
	FSKTXDATA	= 0xc000
	FSKTXCRC	= 0xc001
	FSKTXRAW	= 0xc002
	FSKTXRAWCLR	= 0xc003
	FSKRSSI		= 0xc004
	FSKCTRL		= 0xc008
	FSKSTAT		= 0xc009
	FSKT7FOUT	= 0xc00a
	FSKT7FIN	= 0xc00b
	FSKMDISCTRIS	= 0xc00c
	FSKMDISCOUT	= 0xc00d
	FSKMDISCIN	= 0xc00e

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

	.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

	;; Parameter block at 0xe0
	.ds	0x7a
parbitratetx:	.db	0,0,0
parbitraterx:	.db	0,0,0
parextmodem:	.db	0
parpttmute:	.db	1

	;; Serial# string at 0xf0
	.ds	8
parserial:
	.db	'0,'0,'0,'0,'0,'0,'0,'0,0
	.ds	7

	;; 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

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

	RXCHUNKS = 32
	TXCHUNKS = 16

	DEBUGIOCOPY	= 0
	DEBUGRECEIVER	= 0
	
	.area	OSEG (OVR,DATA)
	.area	BSEG (BIT)
ctrl_ptt:	.ds	1
ctrl_pttmute:	.ds	1
ctrl_ledptt:	.ds	1
ctrl_leddcdsrc:	.ds	1
ctrl_clksel:	.ds	1	; not implemented
ctrl_indacd:	.ds	1
ctrl_indacz:	.ds	1
ctrl_txdsrc:	.ds	1
ctrlreg		=	0x20	; ((ctrl_ptt/8)+0x20)

extmodem:	.ds	1
pttmute:	.ds	1
pttforcechg:	.ds	1
uartempty:	.ds	1
		
	.area	ISEG (DATA)
txbcnt:		.ds	TXCHUNKS
rxbcnt:		.ds	RXCHUNKS
stack:		.ds	0x80-RXCHUNKS-TXCHUNKS
	
	.area	DSEG (DATA)
ctrlcode:	.ds	1
ctrlcount:	.ds	2
leddiv:		.ds	1
irqcount:	.ds	1
	;; transmitter variables
txstate:	.ds	1
flagcnt:	.ds	2
txwr:		.ds	1
txrd:		.ds	1
txtwr:		.ds	1
txcnt:		.ds	1
txaddr:		.ds	2
txbptr:		.ds	1
txskip:		.ds	1
pttforce:	.ds	1
	;; receiver variables
rxstate:	.ds	1
rxwr:		.ds	1
rxrd:		.ds	1
rxtwr:		.ds	1
rxcnt:		.ds	1
rxcntc:		.ds	1
rxaddr:		.ds	2

tmprxcnt:	.ds	1

	;; UART receiver
uartbuf:	.ds	16
uartwr:		.ds	1
uartrd:		.ds	1

	;; Port state
t7fout:		.ds	1
mdisctris:	.ds	1
mdiscout:	.ds	1
	
	;; USB state
numconfig:	.ds	1
altsetting:	.ds	1

	.area	XSEG (DATA)
txbuf:	.ds	TXCHUNKS*64
rxbuf:	.ds	RXCHUNKS*64


	.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	txstate,a
	mov	txwr,a
	mov	txrd,a
	mov	txtwr,a
	mov	pttforce,a
	mov	rxstate,a
	mov	rxwr,a
	mov	rxrd,a
	mov	rxtwr,a
 	mov	irqcount,a
	mov	uartrd,a
	mov	uartwr,a
	mov	dps,a
	setb	uartempty
	;; some indirect register setup
	mov	ckcon,#0x30	; zero external wait states, 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
	;; 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,#0x82		; set PROG hi
	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,#0xc3		; RD/WR/TXD0/RXD0 are special function pins
	movx	@dptr,a
	mov	dptr,#OUTC
	mov	a,#0x28
	movx	@dptr,a
	mov	dptr,#OEC
	mov	a,#0x2a		; out: LEDCON,LEDSTA,TCK  in: TDO,INIT
	movx	@dptr,a
	;; enable interrupts
	mov	ie,#0x92	; enable timer 0 and ser 0 int
	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 T7F communication
	mov	scon0,#0x52	; Mode 1, Timer 1, Receiver enable
	;; Copy serial number
	mov	r0,#parserial
	mov	dptr,#stringserial+2
1$:	movx	a,@r0
	jz	2$
	movx	@dptr,a
	inc	dptr
	inc	dptr
	inc	r0
	sjmp	1$
2$:	mov	a,r0
	add	a,#1-0xf0	; 1-parserial
	add	a,acc
	mov	dptr,#stringserial
	movx	@dptr,a
	;; copy configuration to bit addressable variables
cpyconfig:
	mov	ctrlreg,#0x42
	mov	r0,#parextmodem
	movx	a,@r0
	mov	c,acc.0
	mov	extmodem,c
	mov	ctrl_txdsrc,c
	mov	ctrl_leddcdsrc,c
	mov	r0,#parpttmute
	movx	a,@r0
	mov	c,acc.0
	mov	pttmute,c
	mov	ctrl_pttmute,c
	;; turn off transmitter
	mov	a,ctrlreg
	mov	dptr,#FSKCTRL
	movx	@dptr,a
	;; send reset pulse to external modem if selected
	mov	dptr,#FSKMDISCTRIS
	mov	a,#0xff
	movx	@dptr,a
	mov	mdisctris,a
	jnb	extmodem,3$
	mov	a,#0x67		; RESET, RTS and TxD are outputs
	movx	@dptr,a
	mov	dptr,#FSKMDISCOUT
	mov	a,#0x7f		; activate RESET
	movx	@dptr,a
3$:
	mov	dptr,#FSKT7FOUT
	mov	a,#0x1f
	movx	@dptr,a
	mov	t7fout,a
	;; initialize USB state
	clr	a
	mov	numconfig,a
	mov	altsetting,a
	;; give Windows a chance to finish the writecpucs control transfer
	;; 20ms delay loop
	mov	dptr,#(-12000)&0xffff
2$:	inc	dptr		; 3 cycles
	mov	a,dpl0		; 2 cycles
	orl	a,dph0		; 2 cycles
	jnz	2$		; 3 cycles
	.if	1
	;; 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
	.endif
	;; terminate reset pulse to external modem if selected
	jnb	extmodem,4$
	mov	dptr,#FSKMDISCOUT
	mov	a,#0xff		; deactivate RESET
	movx	@dptr,a
	mov	mdiscout,a
4$:
	;; preinit RAM for debugging purposes (optional)
	.if	0
inittxbuf:
	mov	r7,#TXCHUNKS
	mov	dptr,#txbuf
	mov	a,#0x55+1
0$:	mov	r6,#64
1$:	movx	@dptr,a
	inc	dptr
	djnz	r6,1$
	djnz	r7,0$
	.endif
	.if	0
initrxbuf:
	mov	r7,#RXCHUNKS
	mov	dptr,#rxbuf
	mov	a,#0xaa
0$:	mov	r6,#64
1$:	movx	@dptr,a
	inc	dptr
	djnz	r6,1$
	djnz	r7,0$
	.endif

	;; final
	lcall	fillusbintr
	;; kludge; first OUT2 packet seems to be bogus
	;; wait for packet with length 1 and contents 0x55
waitpkt1:
	mov	dptr,#OUT2CS
	movx	a,@dptr
	jb	acc.1,waitpkt1
	mov	dptr,#OUT2BC
	movx	a,@dptr
	cjne	a,#1,0$
	mov	dptr,#OUT2BUF
	movx	a,@dptr
	cjne	a,#0x55,0$
	mov	dptr,#OUT2BC
	movx	@dptr,a
	sjmp	pkt1received
0$:	mov	dptr,#OUT2BC
	movx	@dptr,a
	sjmp	waitpkt1
	;; clear RX FIFO
pkt1received:
	mov	dptr,#FSKRXFLAG
	mov	r7,#64
1$:	movx	a,@dptr
	djnz	r7,1$
	;; start normal operation
	ljmp	mainloop

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

	;; WARNING!  The assembler doesn't check for
	;; out of range short jump labels!! Double check
	;; that the jump labels are within the range!
	
setptt:	
	mov	a,txstate
	orl	a,pttforce
	jz	pttoff

ptton:
	mov	c,extmodem
	jnc	0$
	mov	dptr,#FSKMDISCOUT
	mov	a,#0xef
	movx	@dptr,a
0$:	mov	ctrl_pttmute,c
	cpl	c
	mov	ctrl_ptt,c
	setb	ctrl_ledptt
	mov	a,ctrlreg
	mov	dptr,#FSKCTRL
	movx	@dptr,a
	ret

pttoff:
	clr	ctrl_ptt
	mov	c,extmodem
	jnc	3$
	mov	dptr,#FSKMDISCOUT
	mov	a,#0xff
	movx	@dptr,a
3$:	orl	c,pttmute
	mov	ctrl_pttmute,c
	clr	ctrl_ledptt
	mov	a,ctrlreg
	mov	dptr,#FSKCTRL
	movx	@dptr,a
	ret

mainloop:
	;; debug: copyloop
	.if	DEBUGIOCOPY
iocopyloop:
	mov	a,txrd
	cjne	a,txwr,0$
	ljmp	iocopyloop
1$:	mov	a,@r0
	mov	@r1,a
	jz	3$
	mov	r7,a
4$:	inc	dps
	movx	a,@dptr
	inc	dptr
	dec	dps
	movx	@dptr,a
	inc	dptr
	djnz	r7,4$
3$:	mov	a,txrd
	inc	a
	anl	a,#(TXCHUNKS-1)
	mov	txrd,a
	mov	a,@r0
	add	a,#-0x40
	jc	5$
	mov	rxwr,rxtwr
5$:	ljmp	iocopyloop
0$:	;; txpointers
	add	a,#txbcnt
	mov	r0,a
	.if	1
	mov	a,txrd
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	add	a,#<txbuf
	xch	a,dpl1
	anl	a,#0x3f
	addc	a,#>txbuf
	mov	dph1,a
	.else
	mov	a,txrd
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	xch	a,dpl1
	anl	a,#0x3f
	add	a,#>txbuf
	mov	dph1,a
	.endif
	;; rxpointers
	mov	a,rxtwr
	add	a,#rxbcnt
	mov	r1,a
	.if	1
	mov	a,rxtwr
	rr	a
	rr	a
	mov	dpl0,a
	anl	a,#0xc0
	add	a,#<rxbuf
	xch	a,dpl0
	anl	a,#0x3f
	addc	a,#>rxbuf
	mov	dph0,a
	.else
	mov	a,rxtwr
	rr	a
	rr	a
	mov	dpl0,a
	anl	a,#0xc0
	xch	a,dpl0
	anl	a,#0x3f
	add	a,#>rxbuf
	mov	dph0,a
	.endif
	;; update rxpointer
	mov	a,rxtwr
	inc	a
	anl	a,#(RXCHUNKS-1)
	mov	rxtwr,a
	cjne	a,rxrd,1$
	ljmp	iocopyloop
	.endif
	;; first do TX
txstartloop:
	;; check if idle or new packet
	mov	a,txstate
	jz	txchknewpkt
	cjne	a,#2,txflagprepare
	ljmp	txchunkstart
txchknewpkt:
	mov	a,txwr
	cjne	a,txrd,txbufnempty
	jbc	pttforcechg,txidlepttforce
rxstartloop1:
	ljmp	rxstartloop
txidlepttforce:
	lcall	setptt
	ljmp	rxstartloop
txbufnempty:
	;; start txdelay
	;; check length of first packet
	mov	a,txrd
	add	a,#txbcnt
	mov	r0,a
	mov	a,@r0
	add	a,#-4
	jc	1$
	ljmp	txnextpacket
1$:
	;; read the number of flags to send from the first two bytes of the first packet
	.if	1
	mov	a,txrd
	rr	a
	rr	a
	mov	dpl0,a
	anl	a,#0xc0
	add	a,#<txbuf
	xch	a,dpl0
	anl	a,#0x3f
	addc	a,#>txbuf
	mov	dph0,a
	.else
	mov	a,txrd
	rr	a
	rr	a
	mov	dpl0,a
	anl	a,#0xc0
	xch	a,dpl0
	anl	a,#0x3f
	add	a,#>txbuf
	mov	dph0,a
	.endif
	movx	a,@dptr
	mov	flagcnt,a
	inc	dptr
	movx	a,@dptr
	mov	flagcnt+1,a
	inc	txstate
	mov	txskip,#2
	;; sanity check (limit txdelay to about 8s)
	mov	r0,#parbitratetx+2
	movx	a,@r0
	jnz	2$
	mov	r0,#parbitratetx+1
	movx	a,@r0
	clr	c
	subb	a,flagcnt+1
	jnc	2$
	movx	a,@r0
	mov	flagcnt+1,a
2$:
	;; turn on PTT
	lcall	ptton
	;; flag send routine
txflagprepare:
	mov	dptr,#FSKTXCNT
	movx	a,@dptr
	jz	rxstartloop1
	mov	r7,a
	;; check if tx ready if external modem
	mov	dptr,#FSKMDISCIN
	movx	a,@dptr
	mov	dptr,#FSKTXRAWCLR
	mov	c,acc.5
	anl	c,extmodem
	jc	txflgcntbig
	;clr	c
	mov	a,flagcnt
	mov	r6,a
	subb	a,r7
	mov	flagcnt,a
	mov	a,flagcnt+1
	subb	a,#0
	mov	flagcnt+1,a
	jnc	txflgcntbig
	;; send final flags, update state
	mov	a,r6
	jz	1$
	mov	a,#0x7e
0$:	movx	@dptr,a
	djnz	r6,0$
1$:	mov	a,txstate
	inc	a
	anl	a,#3
	mov	txstate,a
	jnz	2$
	;; turn off PTT
	lcall	setptt
	ljmp	rxstartloop
2$:	ljmp	txnewchunk
	;; send txdelay flags
txflgcntbig:
	mov	a,#0x7e
0$:	movx	@dptr,a
	djnz	r7,0$
rxstartloop2:
	ljmp	rxstartloop

txnewchunk:
	mov	a,txrd
	add	a,#txbcnt
	mov	txbptr,a
	mov	r0,a
	mov	a,@r0
	cpl	a
	inc	a
	add	a,txskip
	jc	txnextpacket
	mov	txcnt,a
	.if	1
	mov	a,txrd
	rr	a
	rr	a
	mov	txaddr,a
	anl	a,#0xc0
	add	a,txskip
	add	a,#<txbuf
	xch	a,txaddr
	anl	a,#0x3f
	addc	a,#>txbuf
	mov	txaddr+1,a
	.else
	mov	a,txrd
	rr	a
	rr	a
	mov	txaddr,a
	anl	a,#0xc0
	add	a,txskip
	xch	a,txaddr
	anl	a,#0x3f
	add	a,#>txbuf
	mov	txaddr+1,a
	.endif
txchunkstart:
	mov	dptr,#FSKTXCNT
	movx	a,@dptr
	jz	rxstartloop2
	mov	r7,a
	mov	a,txcnt
	jz	txchunkend
	add	a,r7
	xch	a,txcnt
	jnc	0$
	cpl	a
	inc	a
	mov	r7,a
	mov	txcnt,#0
0$:	mov	dptr,#FSKTXDATA
	mov	dpl1,txaddr
	mov	dph1,txaddr+1
1$:	inc	dps
	movx	a,@dptr
	inc	dptr
	dec	dps
	movx	@dptr,a
	djnz	r7,1$
	mov	txaddr,dpl1
	mov	txaddr+1,dph1
	sjmp	txchunkstart
txchunkend:
	mov	txskip,#0
	mov	r0,txbptr
	mov	a,@r0
	jb	acc.6,txnonewframe
	mov	a,r7
	add	a,#-3
	jnc	rxstartloop2
	mov	dptr,#FSKTXCRC
	movx	@dptr,a
	movx	@dptr,a
	mov	dptr,#FSKTXRAWCLR
	mov	a,#0x7e
	movx	@dptr,a
txnextpacket:
	mov	txskip,#2
txnonewframe:
	mov	a,txrd
	inc	a
	anl	a,#TXCHUNKS-1
	mov	txrd,a
	cjne	a,txwr,txnewchunk
	inc	txstate
	;; note: we need to stuff in more than 32 flags, to fill the buffer
	;; (so we don't turn off PTT too early)
	mov	flagcnt,#40
	mov	flagcnt+1,#0
	ljmp	txflagprepare

	.if	DEBUGRECEIVER
rxstartloop:
	mov	dptr,#FSKRXCNT
	movx	a,@dptr
	mov	tmprxcnt,a
	inc	dpl0
	add	a,#-16
	jnc	2$
	.if	1
	mov	a,rxwr
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	add	a,#<rxbuf
	xch	a,dpl1
	anl	a,#0x3f
	addc	a,#>rxbuf
	mov	dph1,a
	.else
	mov	a,rxwr
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	xch	a,dpl1
	anl	a,#0x3f
	add	a,#>rxbuf
	mov	dph1,a	
	.endif
	mov	a,rxwr
	add	a,#rxbcnt
	mov	r0,a
	mov	r7,#0
0$:	movx	a,@dptr
	jb	acc.5,1$
	inc	dps
	movx	@dptr,a
	inc	dptr
	dec	dps
	inc	r7
	jb	acc.7,0$
	movx	a,@dptr
	inc	dps
	movx	@dptr,a
	inc	dptr
	dec	dps
	inc	r7
	sjmp	0$
1$:	mov	@r0,ar7
	mov	a,rxwr
	inc	a
	anl	a,#RXCHUNKS-1
	mov	rxwr,a
	mov	rxtwr,a
2$:	ljmp	txstartloop
	
	.else
	
usbiostart1:
	ljmp	usbiostart
	;; real receiver starts here
rxstartloop:
	mov	dptr,#FSKRXDATA
	mov	a,rxstate
	jnz	rxdataloopstart
	;; hunt for flags
	sjmp	$1
$0:	movx	a,@dptr		; read data value
$1:	movx	a,@dptr		; read control value
	jb	acc.5,usbiostart1
	jnb	acc.7,$0
rxpreparepkt:
	mov	rxcntc,#0
	mov	rxstate,#1
	mov	a,rxwr
	mov	rxtwr,a
rxnewchunk:
	.if	1
	mov	a,rxtwr
	rr	a
	rr	a
	mov	rxaddr,a
	anl	a,#0xc0
	add	a,#<rxbuf
	xch	a,rxaddr
	anl	a,#0x3f
	addc	a,#>rxbuf
	mov	rxaddr+1,a
	.else
	mov	a,rxtwr
	rr	a
	rr	a
	mov	rxaddr,a
	anl	a,#0xc0
	xch	a,rxaddr
	anl	a,#0x3f
	add	a,#>rxbuf
	mov	rxaddr+1,a
	.endif
	mov	rxcnt,#64
	ljmp	rxstartloop	; sjmp would be enough

rxabortframe:
	mov	rxstate,#0
	ljmp	rxstartloop

rxdataloopstart:
	mov	r7,rxcnt
	mov	dpl1,rxaddr
	mov	dph1,rxaddr+1
0$:	movx	a,@dptr		; read command
	jb	acc.6,1$
	movx	a,@dptr
	inc	dps
	movx	@dptr,a
	inc	dptr
	dec	dps
	djnz	r7,0$
	mov	a,rxtwr
	add	a,#rxbcnt
	mov	r0,a
	mov	@r0,#64
	mov	a,rxtwr
	inc	a
	anl	a,#RXCHUNKS-1
	cjne	a,rxrd,2$
	ljmp	rxabortframe
2$:	mov	rxtwr,a
	inc	rxcntc
	sjmp	rxnewchunk
1$:	mov	rxcnt,r7
	mov	rxaddr,dpl1
	mov	rxaddr+1,dph1
	jb	acc.5,usbiostart1	; empty
	jb	acc.4,rxabortframe	; abort received
	anl	a,#0xf
	cjne	a,#0xe,rxpreparepkt	; CRC not ok or residue error
	mov	a,rxtwr
	add	a,#rxbcnt
	mov	r0,a
	mov	a,#64
	clr	c
	subb	a,r7
	mov	@r0,a
	mov	a,rxcntc
	jnz	3$
	mov	a,#-4
	add	a,@r0
	jnc	6$		; frame too short
	sjmp	4$
3$:	add	a,#-8
	jc	6$		; frame too long
4$:	mov	a,rxtwr
	inc	a
	anl	a,#RXCHUNKS-1
	cjne	a,rxrd,5$
	ljmp	rxpreparepkt
5$:	mov	rxwr,a
6$:	ljmp	rxpreparepkt
	.endif

usbiostart:
	;; check for USB modem->host
	mov	a,rxrd
	cjne	a,rxwr,usbcheckin
	;; check for USB host->modem
usbcheckout:
	mov	dptr,#OUT2CS
	movx	a,@dptr
	jb	acc.1,endusb2
	mov	a,txtwr
	add	a,#txbcnt
	mov	r0,a
	mov	a,txtwr
	inc	a
	anl	a,#(TXCHUNKS-1)
	cjne	a,txrd,usbout2
	ljmp	endusb
usbout2:
	mov	r7,a
	mov	dptr,#OUT2BC
	movx	a,@dptr
	mov	@r0,a
	mov	r5,a
	jz	usbout3
	mov	r6,a
	.if	1
	mov	a,txtwr
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	add	a,#<txbuf
	xch	a,dpl1
	anl	a,#0x3f
	addc	a,#>txbuf
	mov	dph1,a
	.else
	mov	a,txtwr
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	xch	a,dpl1
	anl	a,#0x3f
	add	a,#>txbuf
	mov	dph1,a
	.endif
	mov	dptr,#OUT2BUF
usboutloop:
	movx	a,@dptr
	inc	dptr
	inc	dps
	movx	@dptr,a
	inc	dptr
	dec	dps
	djnz	r6,usboutloop
usbout3:
	mov	txtwr,r7
	mov	a,r5
	add	a,#-64
	jz	usbout4
	mov	txwr,r7
usbout4:	
	mov	dptr,#OUT2BC
	movx	@dptr,a		; rearm OUT2
endusb2:
	sjmp	endusb

usbcheckin:
	mov	dptr,#IN2CS
	movx	a,@dptr
	jb	acc.1,usbcheckout

	;ljmp	xxinbuf
	
	mov	a,rxrd
	add	a,#rxbcnt
	mov	r0,a
	mov	a,@r0
	jz	usbin1
	mov	r6,a
	.if	1
	mov	a,rxrd
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	add	a,#<rxbuf
	xch	a,dpl1	
	anl	a,#0x3f
	addc	a,#>rxbuf
	mov	dph1,a
	.else
	mov	a,rxrd
	rr	a
	rr	a
	mov	dpl1,a
	anl	a,#0xc0
	xch	a,dpl1	
	anl	a,#0x3f
	addc	a,#>rxbuf
	mov	dph1,a
	.endif
	mov	dptr,#IN2BUF
usbinloop:
	inc	dps
	movx	a,@dptr
	inc	dptr
	dec	dps
	movx	@dptr,a
	inc	dptr
	djnz	r6,usbinloop
usbin1:
	mov	a,@r0
	mov	dptr,#IN2BC
	movx	@dptr,a
	mov	a,rxrd
	inc	a
	anl	a,#(RXCHUNKS-1)
	mov	rxrd,a
	ljmp	usbcheckout

endusb:
	ljmp	txstartloop


	;; ------------------ 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
	.if	0
	inc	leddiv
	mov	a,leddiv
	anl	a,#7
	jnz	0$
	mov	dptr,#OUTC
	movx	a,@dptr
	xrl	a,#0x08
	movx	@dptr,a
0$:
	.endif
	;; 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
	push	ar0
	;; clear interrupt
	jbc	scon0+0,1$	; RI
0$:	jbc	scon0+1,2$	; TI
	;; handle interrupt
	;; epilogue
3$:	pop	ar0
	pop	dps
	pop	psw
	pop	dph0
	pop	dpl0
	pop	b
	pop	acc
	reti

2$:	setb	uartempty
	sjmp	0$

1$:	mov	a,uartwr
	add	a,#uartbuf
	mov	r0,a
	mov	a,sbuf0
	mov	@r0,a
	mov	a,uartwr
	inc	a
	anl	a,#0xf
	mov	uartwr,a
	sjmp	3$

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

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

cmdnotc0_1:
	ljmp	cmdnotc0
setupstallc0_1:
	ljmp	setupstall
	
cmdnotclrftr:
	;; vendor specific commands
	;; 0xc0
	cjne	a,#0xc0,cmdnotc0_1
	mov	dptr,#SETUPDAT		; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstallc0_1
	;; fill status buffer
	mov	a,txstate
	mov	b,a
	setb	b.2
	orl	a,pttforce
	cjne	a,#0,0$
	clr	b.2
0$:	mov	dptr,#FSKMDISCIN
	movx	a,@dptr
	mov	c,acc.6
	cpl	c
	anl	c,extmodem
	mov	b.3,c
	mov	dptr,#FSKSTAT
	movx	a,@dptr
	mov	c,acc.0
	anl	c,/extmodem
	orl	c,b.3
	mov	b.3,c
	mov	c,uartempty
	mov	b.5,c
	clr	a
	mov	dptr,#(IN0BUF+4)
	movx	@dptr,a
	;; bytewide elements
	mov	dptr,#(IN0BUF)
	mov	a,b
	movx	@dptr,a
	mov	a,txwr
	cpl	a
	add	a,txrd
	anl	a,#(TXCHUNKS-1)
	mov	dptr,#(IN0BUF+1)
	movx	@dptr,a
	mov	a,rxrd
	cpl	a
	add	a,rxwr
	inc	a
	anl	a,#(RXCHUNKS-1)
	mov	dptr,#(IN0BUF+2)
	movx	@dptr,a
	mov	dptr,#FSKRSSI
	movx	a,@dptr
	mov	dptr,#(IN0BUF+3)
	movx	@dptr,a
	;; counter
	inc	irqcount
	mov	a,irqcount
	mov	dptr,#(IN0BUF+5)
	movx	@dptr,a
	;; additional fields (HDLC state mach)
	mov	a,txstate
	inc	dptr
	movx	@dptr,a
	mov	a,txrd
	inc	dptr
	movx	@dptr,a
	mov	a,txwr
	inc	dptr
	movx	@dptr,a
	mov	a,txtwr
	inc	dptr
	movx	@dptr,a
	mov	a,txcnt
	inc	dptr
	movx	@dptr,a
	mov	a,flagcnt
	inc	dptr
	movx	@dptr,a
	mov	a,flagcnt+1
	inc	dptr
	movx	@dptr,a
	mov	a,rxstate
	inc	dptr
	movx	@dptr,a
	mov	a,rxrd
	inc	dptr
	movx	@dptr,a
	mov	a,rxwr
	inc	dptr
	movx	@dptr,a
	mov	a,rxtwr
	inc	dptr
	movx	@dptr,a
	mov	a,rxcnt
	inc	dptr
	movx	@dptr,a
	;; FPGA registers
	mov	a,tmprxcnt
	mov	dptr,#(IN0BUF+18)
	movx	@dptr,a
	mov	dptr,#FSKTXCNT
	movx	a,@dptr
	mov	dptr,#(IN0BUF+19)
	movx	@dptr,a
	inc	dptr
	mov	a,ctrlreg
	movx	@dptr,a
	mov	dptr,#FSKSTAT
	movx	a,@dptr
	mov	dptr,#(IN0BUF+21)
	movx	@dptr,a
	;; Anchor Registers
	mov	dptr,#OUT2CS
	movx	a,@dptr
	mov	dptr,#(IN0BUF+22)
	movx	@dptr,a
	;; set length
	mov	dptr,#SETUPDAT+6	; wLength
	movx	a,@dptr
	add	a,#-(6+12+4+1)
	jnc	4$
	clr	a
4$:	add	a,#(6+12+4+1)
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
cmdnotc0:
	;; 0xc8
	cjne	a,#0xc8,cmdnotc8
	mov	dptr,#SETUPDAT	; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstallc8
	mov	a,#1
	jnb	extmodem,1$
	inc	a
1$:	mov	dptr,#IN0BUF
	movx	@dptr,a
	mov	dptr,#IN0BC
	mov	a,#1
	movx	@dptr,a
	ljmp	setupack
setupstallc8:
	ljmp	setupstall
cmdnotc8:
	;; 0xc9
	cjne	a,#0xc9,cmdnotc9
	mov	dptr,#SETUPDAT	; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstallc9
	mov	dptr,#IN0BUF
	mov	r0,#parserial
0$:	movx	a,@r0
	jz	1$
	movx	@dptr,a
	inc	r0
	inc	dptr
	sjmp	0$
1$:	mov	a,r0
	add	a,#-0xf0	; -parserial
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
setupstallc9:
	ljmp	setupstall
cmdnotc9:
	;; 0xd0
	cjne	a,#0xd0,cmdnotd0
	mov	dptr,#SETUPDAT	; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstalld0
	mov	dptr,#SETUPDAT+4	; wIndex
	movx	a,@dptr
	cjne	a,#1,0$
	mov	dptr,#SETUPDAT+2	; wValue
	movx	a,@dptr
	anl	a,#1
	mov	pttforce,a
	setb	pttforcechg
0$:	;; PTT status
	mov	dptr,#IN0BUF
	mov	a,txstate
	orl	a,pttforce
	jz	1$
	mov	a,#1
1$:	movx	@dptr,a
	;; DCD status
	mov	dptr,#FSKMDISCIN
	movx	a,@dptr
	mov	c,acc.6
	orl	c,/extmodem
	mov	b.0,c
	mov	dptr,#FSKSTAT
	movx	a,@dptr
	mov	c,acc.0
	orl	c,extmodem
	anl	c,b.0
	cpl	c
	clr	a
	mov	acc.0,c
	mov	dptr,#IN0BUF+1
	movx	@dptr,a
	;; RSSI
	mov	dptr,#FSKRSSI
	movx	a,@dptr
	mov	dptr,#IN0BUF+2
	movx	@dptr,a
	;; length
	mov	dptr,#SETUPDAT+6	; wLength
	movx	a,@dptr
	add	a,#-3
	jnc	2$
	clr	a
2$:	add	a,#3
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
setupstalld0:
	ljmp	setupstall
cmdnotd0:
	;; 0xd1
	cjne	a,#0xd1,cmdnotd1
	mov	dptr,#SETUPDAT	; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstalld1
	mov	dptr,#IN0BUF
	mov	r0,#parbitratetx
	mov	r7,#6
1$:	movx	a,@r0
	movx	@dptr,a
	inc	dptr
	inc	r0
	djnz	r7,1$
	;; length
	mov	dptr,#SETUPDAT+6	; wLength
	movx	a,@dptr
	add	a,#-6
	jnc	2$
	clr	a
2$:	add	a,#6
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
setupstalld1:
	ljmp	setupstall
cmdnotd1:
	;; 0xd2
	cjne	a,#0xd2,cmdnotd2
	mov	dptr,#SETUPDAT	; bRequestType == 0x40
	movx	a,@dptr
	cjne	a,#0x40,setupstalld2
	mov	dptr,#SETUPDAT+2	; wValue
	movx	a,@dptr
	mov	b,a
	mov	dptr,#OUTC
	movx	a,@dptr
	mov	c,b.0
	mov	acc.3,c
	mov	c,b.1
	mov	acc.5,c
	movx	@dptr,a
	ljmp	setupack
setupstalld2:
	ljmp	setupstall
cmdnotd2:
	;; 0xd3
	cjne	a,#0xd3,cmdnotd3
	mov	dptr,#SETUPDAT	; bRequestType == 0x40
	movx	a,@dptr
	cjne	a,#0x40,setupstalld3
	mov	dptr,#SETUPDAT+2	; wValue
	movx	a,@dptr
	jbc	uartempty,cmdd2cont
setupstalld3:
	ljmp	setupstall
cmdd2cont:
	mov	sbuf0,a
	ljmp	setupack
cmdnotd3:
	;; 0xd4
	cjne	a,#0xd4,cmdnotd4
	mov	dptr,#SETUPDAT	; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstalld4
	jb	extmodem,setupstalld4
	mov	dptr,#SETUPDAT+4	; wIndex
	movx	a,@dptr
	mov	dptr,#SETUPDAT+2	; wValue
	cjne	a,#1,0$
	movx	a,@dptr
	mov	dptr,#FSKMDISCOUT
	movx	@dptr,a
	mov	mdiscout,a
	sjmp	1$
0$:	cjne	a,#2,1$
	movx	a,@dptr
	mov	dptr,#FSKMDISCTRIS
	movx	@dptr,a
	mov	mdisctris,a
1$:	mov	dptr,#FSKMDISCIN
	movx	a,@dptr
	mov	dptr,#IN0BUF+0
	movx	@dptr,a
	mov	a,mdiscout
	inc	dptr
	movx	@dptr,a
	mov	a,mdisctris
	inc	dptr
	movx	@dptr,a
	;; length
	mov	dptr,#SETUPDAT+6	; wLength
	movx	a,@dptr
	add	a,#-3
	jnc	2$
	clr	a
2$:	add	a,#3
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
setupstalld4:
	ljmp	setupstall
cmdnotd4:
	;; 0xd5
	cjne	a,#0xd5,cmdnotd5
	mov	dptr,#SETUPDAT	; bRequestType == 0xc0
	movx	a,@dptr
	cjne	a,#0xc0,setupstalld5
	mov	dptr,#SETUPDAT+4	; wIndex
	movx	a,@dptr
	cjne	a,#1,0$
	mov	dptr,#SETUPDAT+2	; wValue
	movx	a,@dptr
	mov	dptr,#FSKT7FOUT
	movx	@dptr,a
	mov	t7fout,a
0$:	mov	dptr,#FSKT7FIN
	movx	a,@dptr
	mov	dptr,#IN0BUF+0
	movx	@dptr,a
	mov	a,t7fout
	inc	dptr
	movx	@dptr,a
	;; length
	mov	dptr,#SETUPDAT+6	; wLength
	movx	a,@dptr
	add	a,#-2
	jnc	2$
	clr	a
2$:	add	a,#2
	mov	dptr,#IN0BC
	movx	@dptr,a
	ljmp	setupack
setupstalld5:
	ljmp	setupstall
cmdnotd5:
	;; 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

;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

;ep0outstall:
;	mov	ctrlcode,#0
;	mov	a,#3
;	sjmp	ep0outcs
;ep0outack:
;	mov	txwr,txtwr
;	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	a,txstate
	mov	b,a
	setb	b.2
	orl	a,pttforce
	cjne	a,#0,0$
	clr	b.2
0$:	mov	dptr,#FSKMDISCIN
	movx	a,@dptr
	mov	c,acc.6
	cpl	c
	anl	c,extmodem
	mov	b.3,c
	mov	dptr,#FSKSTAT
	movx	a,@dptr
	mov	c,acc.0
	anl	c,/extmodem
	orl	c,b.3
	mov	b.3,c
	mov	c,uartempty
	mov	b.5,c
	setb	c
	mov	a,rxrd
	cjne	a,rxwr,1$
	mov	dptr,#(IN2CS)
	movx	a,@dptr
	mov	c,acc.1
1$:	mov	b.4,c
	;; bytewide elements
	mov	dptr,#(IN1BUF)
	mov	a,b
	movx	@dptr,a
	mov	a,txwr
	cpl	a
	add	a,txrd
	anl	a,#(TXCHUNKS-1)
	mov	dptr,#(IN1BUF+1)
	movx	@dptr,a
	mov	a,rxrd
	cpl	a
	add	a,rxwr
	inc	a
	anl	a,#(RXCHUNKS-1)
	mov	dptr,#(IN1BUF+2)
	movx	@dptr,a
	mov	dptr,#FSKRSSI
	movx	a,@dptr
	mov	dptr,#(IN1BUF+3)
	movx	@dptr,a
	; counter
	inc	irqcount
	mov	a,irqcount
	mov	dptr,#(IN1BUF+4)
	movx	@dptr,a
	; UART buffer
	mov	b,#5
	mov	dptr,#(IN1BUF+5)
2$:	mov	a,uartrd
	cjne	a,uartwr,3$
	; set length
	mov	dptr,#IN1BC
	mov	a,b
	movx	@dptr,a
	ret
	
3$:	add	a,#uartbuf
	mov	r0,a
	mov	a,@r0
	movx	@dptr,a
	inc	dptr
	inc	b
	mov	a,uartrd
	inc	a
	anl	a,#0xf
	mov	uartrd,a
	sjmp	2$

	
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	= 0x6136

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,'F,0,'S,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

