;   usbduxfast_firmware.asm
;   Copyright (C) 2004,2009 Bernd Porr, Bernd.Porr@f2s.com
;
;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation; either version 2 of the License, or
;   (at your option) any later version.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;
;
; Firmware: usbduxfast_firmware.asm for usbdux.c
; Description: Firmware for usbduxfast
; Devices: [ITL] USB-DUX (usbdux.o)
; Author: Bernd Porr <Bernd.Porr@f2s.com>
; Updated: 17 Apr 2009
; Status: stable
;
;;;
;;;
;;;

	.inc	fx2-include.asm

	.equ	WFLOADED,70H	; waveform is loaded

	.org	0000h		; after reset the processor starts here
	ljmp	main		; jump to the main loop

	.org	0043h		; the IRQ2-vector
	ljmp	jmptbl		; irq service-routine

	.org	0100h		; start of the jump table

jmptbl:	ljmp	sudav_isr
	nop
	ljmp	sof_isr
	nop
	ljmp	sutok_isr
	nop
	ljmp	suspend_isr
	nop
	ljmp	usbreset_isr
	nop
	ljmp	hispeed_isr
	nop
	ljmp	ep0ack_isr
	nop
	ljmp	spare_isr
	nop
	ljmp	ep0in_isr
	nop
	ljmp	ep0out_isr
	nop
	ljmp	ep1in_isr
	nop
	ljmp	ep1out_isr
	nop
	ljmp	ep2_isr
	nop
	ljmp	ep4_isr
	nop
	ljmp	ep6_isr
	nop
	ljmp	ep8_isr
	nop
	ljmp	ibn_isr
	nop
	ljmp	spare_isr
	nop
	ljmp	ep0ping_isr
	nop
	ljmp	ep1ping_isr
	nop
	ljmp	ep2ping_isr
	nop
	ljmp	ep4ping_isr
	nop
	ljmp	ep6ping_isr
	nop
	ljmp	ep8ping_isr
	nop
	ljmp	errlimit_isr
	nop
	ljmp	spare_isr
	nop
	ljmp	spare_isr
	nop
	ljmp	spare_isr
	nop
	ljmp	ep2isoerr_isr
	nop
	ljmp	ep4isoerr_isr
	nop
	ljmp	ep6isoerr_isr
	nop
	ljmp	ep8isoerr_isr

	
	;; dummy isr
sof_isr:
sudav_isr:	
sutok_isr:	
suspend_isr:	
usbreset_isr:	
hispeed_isr:	
ep0ack_isr:	
spare_isr:	
ep0in_isr:	
ep0out_isr:	
ep1out_isr:
ep1in_isr:	
ibn_isr:	
ep0ping_isr:	
ep1ping_isr:	
ep2ping_isr:	
ep4ping_isr:	
ep6ping_isr:	
ep8ping_isr:	
errlimit_isr:	
ep2isoerr_isr:	
ep4isoerr_isr:	
ep6isoerr_isr:	
ep8isoerr_isr:
ep6_isr:
ep2_isr:
ep8_isr:

	push	dps
	push	dpl
	push	dph
	push	dpl1
	push	dph1
	push	acc
	push	psw

	;; clear the USB2 irq bit and return
	mov	a,EXIF
	clr	acc.4
	mov	EXIF,a

	pop	psw
	pop	acc 
	pop	dph1 
	pop	dpl1
	pop	dph 
	pop	dpl 
	pop	dps
	
	reti

		
;;; main program
;;; basically only initialises the processor and
;;; then engages in an endless loop
main:
	mov	dptr,#REVCTL
	mov	a,#00000011b	; allows skip
	lcall	syncdelaywr

	mov	DPTR,#CPUCS	; CPU control register
	mov	a,#00010000b	; 48Mhz
	lcall	syncdelaywr

	mov	dptr,#IFCONFIG	; switch on IFCLK signal
	mov	a,#10100010b	; gpif, 30MHz
	lcall	syncdelaywr

	mov	dptr,#FIFORESET
	mov	a,#80h
	lcall	syncdelaywr
	mov	a,#8
	lcall	syncdelaywr
	mov	a,#2		
	lcall	syncdelaywr
	mov	a,#4		
	lcall	syncdelaywr
	mov	a,#6		
	lcall	syncdelaywr
	mov	a,#0		
	lcall	syncdelaywr

	mov	dptr,#INTSETUP	; IRQ setup register
	mov	a,#08h		; enable autovector
	lcall	syncdelaywr

	lcall	initeps		; init the isochronous data-transfer

	lcall	initGPIF

;;; main loop

mloop2:
	lcall	gpif_run
	sjmp	mloop2		; do nothing. The rest is done by the IRQs


gpif_run:
	mov	a,WFLOADED
	jz	no_trig		; do not trigger
	mov	a,GPIFTRIG	; GPIF status
	anl	a,#80h		; done bit
	jz	no_trig		; GPIF busy

;;; gpif has stopped
	mov	a,#06h		; RD,EP6
	mov	GPIFTRIG,a
no_trig:
	ret

	

initGPIF:
	mov	DPTR,#EP6CFG	; BLK data from here to the host
	mov	a,#11100000b	; Valid, quad buffering
	lcall	syncdelaywr	; write

	mov	dptr,#EP6FIFOCFG
	mov	a,#00001001b	; autoin, wordwide
	lcall	syncdelaywr

	mov	dptr,#EP6AUTOINLENH
	mov	a,#00000010b	; 512 bytes
	lcall	syncdelaywr	; write

	mov	dptr,#EP6AUTOINLENL
	mov	a,#00000000b	; 0
	lcall	syncdelaywr	; write

	mov	dptr,#GPIFWFSELECT
	mov	a,#11111100b	; waveform 0 for FIFO RD
	lcall	syncdelaywr

	mov	dptr,#GPIFCTLCFG
	mov	a,#10000000b	; tri state for CTRL
	lcall	syncdelaywr

	mov	dptr,#GPIFIDLECTL
	mov	a,#11111111b	; all CTL outputs high
	lcall	syncdelaywr
	mov	a,#11111101b	; reset counter
	lcall	syncdelaywr
	mov	a,#11111111b	; reset to high again
	lcall	syncdelaywr

	mov	a,#00000010b	; abort when full
	mov	dptr,#EP6GPIFFLGSEL
	lcall	syncdelaywr

	mov	a,#00000001b	; stop when buffer overfl
	mov	dptr,#EP6GPIFPDFSTOP
	lcall	syncdelaywr

	mov	a,#0
	mov	dptr,#GPIFREADYCFG
	lcall	syncdelaywr

	mov	a,#0
	mov	dptr,#GPIFIDLECS
	lcall	syncdelaywr

; waveform 1
; this is a dummy waveform which is used
; during the upload of another waveform into
; wavefrom 0
; it branches directly into the IDLE state
	mov	dptr,#0E420H
	mov	a,#00111111b	; branch to IDLE
	lcall	syncdelaywr

	mov	dptr,#0E428H	; opcode
	mov	a,#00000001b	; deceision point
	lcall	syncdelaywr

	mov	dptr,#0E430H
	mov	a,#0FFH		; output is high
	lcall	syncdelaywr

	mov	dptr,#0E438H
	mov	a,#0FFH		; logic function
	lcall	syncdelaywr

; signals that no waveform 0 is loaded so far
	mov	WFLOADED,#0	; waveform flag

	ret



;;; initilise the transfer
;;; It is assumed that the USB interface is in alternate setting 1
initeps:
	mov	DPTR,#EP4CFG
	mov	a,#10100000b	; valid, bulk, out
	lcall	syncdelaywr

	mov	dptr,#EP4BCL	; "arm" it
	mov	a,#00h
	lcall	syncdelaywr	; wait until we can write again
	lcall	syncdelaywr	; wait
	lcall	syncdelaywr	; wait

	mov	DPTR,#EP8CFG
	mov	a,#0		; disable EP8, it overlaps with EP6!!
	lcall	syncdelaywr

	mov	dptr,#EPIE	; interrupt enable
	mov	a,#00100000b	; enable irq for ep4
	lcall	syncdelaywr	; do it

	mov	dptr,#EPIRQ	; clear IRQs
	mov	a,#00100100b
	movx	@dptr,a

        mov     DPTR,#USBIE	; USB int enable register
        mov     a,#0            ; SOF etc
        movx    @DPTR,a         ;

        mov     DPTR,#GPIFIE	; GPIF int enable register
        mov     a,#0            ; done IRQ
        movx    @DPTR,a         ;

	mov	EIE,#00000001b	; enable INT2 in the 8051's SFR
	mov	IE,#80h		; IE, enable all interrupts

	ret


;;; interrupt-routine for ep4
;;; receives the channel list and other commands
ep4_isr:
	push	dps
	push	dpl
	push	dph
	push	dpl1
	push	dph1
	push	acc
	push	psw
	push	00h		; R0
	push	01h		; R1
	push	02h		; R2
	push	03h		; R3
	push	04h		; R4
	push	05h		; R5
	push	06h		; R6
	push	07h		; R7
		
	mov	dptr,#0f400h	; FIFO buffer of EP4
	movx	a,@dptr		; get the first byte

	mov	dptr,#ep4_jmp	; jump table for the different functions
	rl	a		; multiply by 2: sizeof sjmp
	jmp	@a+dptr		; jump to the jump table

ep4_jmp:
	sjmp	storewaveform	; a=0
	sjmp	init_ep6	; a=1
	
init_ep6:
	; stop ep6
	; just now do nothing

	ljmp	over_wf


storewaveform:
	mov	WFLOADED,#0	; waveform flag

	mov	dptr,#EP6FIFOCFG
	mov	a,#00000000b	;
	lcall	syncdelaywr

	mov	dptr,#GPIFABORT
	mov	a,#0ffh		; abort all transfers
	lcall	syncdelaywr

wait_f_abort:
	mov	a,GPIFTRIG	; GPIF status
	anl	a,#80h		; done bit
	jz	wait_f_abort	; GPIF busy

	mov	dptr,#GPIFWFSELECT
	mov	a,#11111101b	; select dummy waveform
	movx	@dptr,a
	lcall	syncdelay

	mov	dptr,#FIFORESET
	mov	a,#80h		; NAK
	lcall	syncdelaywr
	mov	a,#6		; reset EP6
	lcall	syncdelaywr
	mov	a,#0		; normal op
	lcall	syncdelaywr

; change to dummy waveform 1
	mov	a,#06h		; RD,EP6
	mov	GPIFTRIG,a

; wait a bit
	mov	r2,255
loopx:
	djnz	r2,loopx

; abort waveform if not already so
	mov	dptr,#GPIFABORT
	mov	a,#0ffh		; abort all transfers
	lcall	syncdelaywr

; wait again
	mov	r2,255
loopx2:
	djnz	r2,loopx2

; check for DONE
wait_f_abort2:
	mov	a,GPIFTRIG	; GPIF status
	anl	a,#80h		; done bit
	jz	wait_f_abort2	; GPIF busy

; upload the new waveform into waveform 0
	mov	AUTOPTRH2,#0E4H	; XDATA0H
	lcall	syncdelay
	mov	AUTOPTRL2,#00H	; XDATA0L
	lcall	syncdelay

	mov	AUTOPTRH1,#0F4H	; EP4 high
	lcall	syncdelay
	mov	AUTOPTRL1,#01H	; EP4 low
	lcall	syncdelay

	mov	AUTOPTRSETUP,#7	; autoinc and enable
	lcall	syncdelay

	mov 	r2,#20H		; 32 bytes to transfer

wavetr:
	mov 	dptr,#XAUTODAT1
	movx	a,@dptr
	lcall	syncdelay
	mov	dptr,#XAUTODAT2
	movx	@dptr,a
	lcall	syncdelay
	djnz	r2,wavetr

	mov	dptr,#EP6FIFOCFG
	mov	a,#00001001b	; autoin, wordwide
	lcall	syncdelaywr

	mov	dptr,#GPIFWFSELECT
	mov	a,#11111100b
	movx	@dptr,a
	lcall	syncdelay

	mov	dptr,#FIFORESET
	mov	a,#80h		; NAK
	lcall	syncdelaywr
	mov	a,#6		; reset EP6
	lcall	syncdelaywr
	mov	a,#0		; normal op
	lcall	syncdelaywr

	mov	dptr,#0E400H+10H; waveform 0: first CTL byte
	movx	a,@dptr		; get it
	orl	a,#11111011b	; force all bits to one except the range bit
	mov	dptr,#GPIFIDLECTL
	lcall	syncdelaywr

	mov	WFLOADED,#1	; waveform flag

; do the common things here	
over_wf:	
	mov	dptr,#EP4BCL
	mov	a,#00h
	movx	@DPTR,a		; arm it
	lcall	syncdelay	; wait
	movx	@DPTR,a		; arm it
	lcall	syncdelay	; wait

	;; clear INT2
	mov	a,EXIF		; FIRST clear the USB (INT2) interrupt request
	clr	acc.4
	mov	EXIF,a		; Note: EXIF reg is not 8051 bit-addressable

	mov	DPTR,#EPIRQ	; 
	mov	a,#00100000b	; clear the ep4irq
	movx	@DPTR,a

	pop	07h
	pop	06h
	pop	05h
	pop	04h		; R4
	pop	03h		; R3
	pop	02h		; R2
	pop	01h		; R1
	pop	00h		; R0
	pop	psw
	pop	acc 
	pop	dph1 
	pop	dpl1
	pop	dph 
	pop	dpl 
	pop	dps
	reti


;; need to delay every time the byte counters
;; for the EPs have been changed.

syncdelay:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	ret


syncdelaywr:
	lcall	syncdelay
	movx	@dptr,a
	ret


.End