]> git.proxmox.com Git - mirror_linux-firmware.git/commitdiff
Add the source code for the usbdux firmware
authorBernd Porr <BerndPorr@f2s.com>
Wed, 22 Apr 2009 23:23:33 +0000 (16:23 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 22 Apr 2009 23:23:33 +0000 (16:23 -0700)
From: Bernd Porr <BerndPorr@f2s.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
usbdux/Makefile_dux [new file with mode: 0644]
usbdux/README.dux [new file with mode: 0644]
usbdux/fx2-include.asm [new file with mode: 0644]
usbdux/usbdux_firmware.asm [new file with mode: 0644]
usbdux/usbduxfast_firmware.asm [new file with mode: 0644]

diff --git a/usbdux/Makefile_dux b/usbdux/Makefile_dux
new file mode 100644 (file)
index 0000000..3098d5b
--- /dev/null
@@ -0,0 +1,19 @@
+# (c) Bernd Porr
+# GNU public license
+# no warranty
+#
+
+all: as31 usbduxfast_firmware.bin usbdux_firmware.bin
+
+as31:
+       make -C as31-2.1
+
+usbduxfast_firmware.bin: fx2-include.asm usbduxfast_firmware.asm as31
+       as31-2.1/as31 -Fbin usbduxfast_firmware.asm
+
+usbdux_firmware.bin: fx2-include.asm usbdux_firmware.asm as31
+       as31-2.1/as31 -Fbin usbdux_firmware.asm
+
+clean:
+       rm -f *.bin *~ *.lst *.bin
+       make -C as31-2.1 clean
diff --git a/usbdux/README.dux b/usbdux/README.dux
new file mode 100644 (file)
index 0000000..6ff3c18
--- /dev/null
@@ -0,0 +1,15 @@
+To compile the firmware for the USBDUX and USBDUXfast
+-----------------------------------------------------
+Download the as31 from:
+
+http://www.berndporr.me.uk/as31/
+
+(this is a patched version of as31 which supports an
+include directive).
+
+Install it in this directory.
+
+Then run "make -f Makefile_dux".
+
+Feedback:
+BerndPorr@f2s.com
diff --git a/usbdux/fx2-include.asm b/usbdux/fx2-include.asm
new file mode 100644 (file)
index 0000000..7da6975
--- /dev/null
@@ -0,0 +1,164 @@
+; rev 0.9
+; (c) Bernd Porr, Bernd.Porr@cn.stir.ac.uk
+; GPL, GNU public license
+;
+;   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.
+;
+; In conjunction with the as31.
+; Include-file for the FX2 by Cypress. The rest of the regs is defined
+; by the as31 itself.
+;
+; from the TRM of the FX2:
+;
+       ;;  CPU control
+       .equ    CPUCS,0E600H
+       .equ    REVCTL,0E60BH
+       ;; interface config
+       .equ    IFCONFIG,0E601H
+       .equ    FIFORESET,0E604H
+       ;; Endpoint configs
+       .equ    EP1OUTCFG,0E610H
+       .equ    EP1INCFG,0E611H
+       .equ    EP2CFG,0E612H
+       .equ    EP4CFG,0E613H
+       .equ    EP6CFG,0E614H
+       .equ    EP8CFG,0E615H
+       ;; packets per frame, always one for USB 1.1
+       .equ    EP2ISOINPKTS,0E640H
+       .equ    EP4ISOINPKTS,0E641H
+       .equ    EP6ISOINPKTS,0E642H
+       .equ    EP8ISOINPKTS,0E643H
+       ;; endpoint byte counts
+       .equ    EP1OUTBC,0E68DH
+       .equ    EP1INBC,0E68FH
+       .equ    EP1INCS,0E6A2H
+       .equ    EP2BCH,0E690H
+       .equ    EP2BCL,0E691H
+       .equ    EP4BCH,0E694H
+       .equ    EP4BCL,0E695H
+       .equ    EP6BCH,0E698H
+       .equ    EP6BCL,0E699H
+       .equ    EP8BCH,0E69CH
+       .equ    EP8BCL,0E69DH
+       ;;
+       .equ    EP4AUTOINLENH,0E622H
+       .equ    EP4AUTOINLENL,0E623H
+       .equ    EP6AUTOINLENH,0E624H
+       .equ    EP6AUTOINLENL,0E625H
+       .equ    EP2FIFOCFG,0E618H
+       .equ    EP4FIFOCFG,0E619H
+       .equ    EP6FIFOCFG,0E61AH
+       .equ    EP8FIFOCFG,0E61BH
+       ;; 
+       .equ    INPKTEND,0E648H
+       .equ    GPIFCTLCFG,0E6C3H
+       .equ    GPIFABORT,0E6F5H
+       .equ    GPIFIDLECTL,0E6C2H
+       .equ    GPIFWFSELECT,0E6C0H
+       .equ    GPIFREADYCFG,0E6F3H
+       .equ    GPIFIDLECS,0E6C1H
+       .equ    EP6GPIFFLGSEL,0E6E2H
+       .equ    EP6GPIFPDFSTOP,0E6E3H
+       .equ    EP6GPIFTRIG,0E6E4H
+       .equ    GPIFIE,0E660H
+       .equ    GPIFIRQ,0E661H
+       ;; 
+       ;; endpoint control
+       .equ    EP2CS,0E6A3H
+       .equ    EP4CS,0E6A4H
+       .equ    EP6CS,0E6A5H
+       .equ    EP8CS,0E6A6H
+       ;; endpoint buffers
+       .equ    EP2FIFOBUF,0F000H
+       .equ    EP4FIFOBUF,0F400H
+       .equ    EP6FIFOBUF,0F800H
+       .equ    EP8FIFOBUF,0FC00H
+       ;; IRQ enable for bulk NAK
+       .equ    IBNIE,0E658H
+       ;; interrupt requ for NAK
+       .equ    IBNIRQ,0E659H
+       ;; USB INT enables
+       .equ    USBIE,0E65CH
+       ;; USB interrupt request
+       .equ    USBIRQ,0E65DH
+       ;; endpoint IRQ enable
+       .equ    EPIE,0E65EH
+       ;; endpoint IRQ requests
+       .equ    EPIRQ,0E65FH
+       ;; USB error IRQ requests
+       .equ    USBERRIE,0E662H
+       ;; USB error IRQ request
+       .equ    USBERRIRQ,0E663H
+       ;; USB interrupt 2 autovector
+       .equ    INT2IVEC,0E666H
+       ;; autovector enable
+       .equ    INTSETUP,0E668H
+       ;; port cfg
+       .equ    PORTACFG,0E670H
+       .equ    PORTCCFG,0E671H
+       .equ    PORTECFG,0E672H
+       ;; I2C bus
+       .equ    I2CS,0E678H
+       .equ    I2DAT,0E679H
+       .equ    I2CTL,0E67AH
+       ;; auto pointers, read/write is directed to the pointed address
+       .equ    XAUTODAT1,0E67BH
+       .equ    XAUTODAT2,0E67CH
+       ;; USB-control
+       .equ    USBCS,0E680H
+       ;; force packet end
+       .equ    OUTPKTEND,0E649H
+       .equ    IOA,80H
+       .equ    DPL1,84H
+       .equ    DPH1,85H
+       .equ    DPS,86H
+       .equ    CKCON,8Eh
+       .equ    IOB,90H
+       .equ    EXIF,91h
+       .equ    MPAGE,92h
+       .equ    AUTOPTRH1,9AH
+       .equ    AUTOPTRL1,9BH
+       .equ    AUTOPTRH2,9DH
+       .equ    AUTOPTRL2,9EH
+       .equ    IOC,0A0H
+       .equ    INT2CLR,0A1H
+       .equ    INT4CLR,0A2H
+       .equ    EP2468STAT,0AAH
+       .equ    EP24FIFOFLGS,0ABH
+       .equ    EP68FIFOFLGS,0ACH
+       .equ    AUTOPTRSETUP,0AFH
+       .equ    IOD,0B0H
+       .equ    IOE,0B1H
+       .equ    OEA,0B2H
+       .equ    OEB,0B3H
+       .equ    OEC,0B4H
+       .equ    OED,0B5H
+       .equ    OEE,0B6H
+       .equ    GPIFTRIG,0BBH
+       .equ    EIE,0E8h
+       .equ    EIP,0F8h
+
+       
+       ;; GPIF
+       .equ    GPIFTCB3,0E6CEH
+       .equ    GPIFTCB2,0E6CFH
+       .equ    GPIFTCB1,0E6D0H
+       .equ    GPIFTCB0,0E6D1H
+       .equ    EP4GPIFFLGSEL,0E6DAH
+       .equ    EP4GPIFPFSTOP,0E6DBH
+
+
+       ;;; end of file
+       
diff --git a/usbdux/usbdux_firmware.asm b/usbdux/usbdux_firmware.asm
new file mode 100644 (file)
index 0000000..b93a895
--- /dev/null
@@ -0,0 +1,1184 @@
+;   usbdux_firmware.asm
+;   Copyright (C) 2004,2009 Bernd Porr, Bernd.Porr@f2s.com
+;   For usbdux.c
+;
+;   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: usbdux_firmware.asm for usbdux.c
+; Description: University of Stirling USB DAQ & INCITE Technology Limited
+; Devices: [ITL] USB-DUX (usbdux.o)
+; Author: Bernd Porr <Bernd.Porr@f2s.com>
+; Updated: 17 Apr 2009
+; Status: stable
+;
+;;;
+;;;
+;;;
+
+       .inc    fx2-include.asm
+
+       .equ    CHANNELLIST,80h ; channellist in indirect memory
+       
+       .equ    CMD_FLAG,90h    ; flag if next IN transf is DIO
+       .equ    SGLCHANNEL,91h  ; channel for INSN
+       .equ    PWMFLAG,92h     ; PWM
+       
+       .equ    DIOSTAT0,98h    ; last status of the digital port
+       .equ    DIOSTAT1,99h    ; same for the second counter
+       
+       .equ    CTR0,0A0H       ; counter 0
+       .equ    CTR1,0A2H       ; counter 1
+                       
+       .org    0000h           ; after reset the processor starts here
+       ljmp    main            ; jump to the main loop
+
+       .org    000bh           ; timer 0 irq
+       ljmp    timer0_isr
+
+       .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
+sudav_isr:     
+sutok_isr:     
+suspend_isr:   
+usbreset_isr:  
+hispeed_isr:   
+ep0ack_isr:    
+spare_isr:     
+ep0in_isr:     
+ep0out_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:
+ep4_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,#CPUCS     ; CPU control register
+       mov     a,#00010000b    ; 48Mhz
+       lcall   syncdelaywr
+
+        mov     dptr,#REVCTL
+        mov     a,#00000011b    ; allows skip
+        lcall   syncdelaywr
+
+       mov     IP,#0           ; all std 8051 int have low priority
+       mov     EIP,#0FFH       ; all FX2 interrupts have high priority
+       
+       mov     dptr,#INTSETUP  ; IRQ setup register
+       mov     a,#08h          ; enable autovector
+       lcall   syncdelaywr
+
+       lcall   initAD          ; init the ports to the converters
+
+       lcall   initeps         ; init the isochronous data-transfer
+
+       lcall   init_timer
+       
+mloop2:        nop
+
+;;; pwm
+       mov     r0,#PWMFLAG     ; pwm on?
+       mov     a,@r0           ; get info
+       jz      mloop2          ; it's off
+
+       mov     a,GPIFTRIG      ; GPIF status
+       anl     a,#80h          ; done bit
+       jz      mloop2          ; GPIF still busy
+
+        mov     a,#01h         ; WR,EP4, 01 = EP4
+        mov     GPIFTRIG,a     ; restart it
+
+       sjmp    mloop2          ; loop for ever
+
+
+;;; GPIF waveform for PWM
+waveform:
+       ;;      0     1     2     3     4     5     6     7(not used)
+       ;; len (gives 50.007Hz)
+       .db     195,  195,  195,  195,  195,  195,  1,    1
+
+       ;; opcode
+       .db     002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
+       
+       ;; out
+       .db     0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
+
+       ;; log
+       .db     000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
+
+
+stopPWM:
+       mov     r0,#PWMFLAG     ; flag for PWM
+       mov     a,#0            ; PWM (for the main loop)
+       mov     @r0,a           ; set it
+
+       mov     dptr,#IFCONFIG  ; switch off GPIF
+       mov     a,#10000000b    ; gpif, 30MHz, internal IFCLK
+       lcall   syncdelaywr
+       ret
+       
+
+;;; init PWM
+startPWM:
+       mov     dptr,#IFCONFIG  ; switch on IFCLK signal
+       mov     a,#10000010b    ; gpif, 30MHz, internal IFCLK
+       lcall   syncdelaywr
+
+       mov     OEB,0FFH        ; output to port B
+
+       mov     DPTR,#EP4CFG
+       mov     a,#10100000b    ; valid, out, bulk
+       movx    @DPTR,a
+
+       ;; reset the endpoint
+       mov     dptr,#FIFORESET
+       mov     a,#80h          ; NAK
+       lcall   syncdelaywr
+       mov     a,#84h          ; reset EP4 + NAK
+       lcall   syncdelaywr
+       mov     a,#0            ; normal op
+       lcall   syncdelaywr
+
+       mov     dptr,#EP4BCL
+       mov     a,#0H           ; discard packets
+       lcall   syncdelaywr     ; empty FIFO buffer
+       lcall   syncdelaywr     ; empty FIFO buffer
+
+       ;; aborts all transfers by the GPIF
+       mov     dptr,#GPIFABORT
+       mov     a,#0ffh         ; abort all transfers
+       lcall   syncdelaywr
+
+       ;; wait for GPIF to finish
+wait_f_abort:
+       mov     a,GPIFTRIG      ; GPIF status
+       anl     a,#80h          ; done bit
+       jz      wait_f_abort    ; GPIF busy
+
+        mov     dptr,#GPIFCTLCFG
+        mov     a,#10000000b    ; tri state for CTRL
+        lcall   syncdelaywr
+
+        mov     dptr,#GPIFIDLECTL
+        mov     a,#11110000b    ; all CTL outputs low
+        lcall   syncdelaywr
+
+       ;; abort if FIFO is empty
+        mov     a,#00000001b    ; abort if empty
+        mov     dptr,#EP4GPIFFLGSEL
+        lcall   syncdelaywr
+
+       ;; 
+        mov     a,#00000001b    ; stop if GPIF flg
+        mov     dptr,#EP4GPIFPFSTOP
+        lcall   syncdelaywr
+
+       ;; transaction counter
+       mov     a,#0ffH
+       mov     dptr,#GPIFTCB3
+       lcall   syncdelaywr
+
+       ;; transaction counter
+       mov     a,#0ffH
+       mov     dptr,#GPIFTCB2
+       lcall   syncdelaywr
+
+       ;; transaction counter
+       mov     a,#0ffH         ; 512 bytes
+       mov     dptr,#GPIFTCB1
+       lcall   syncdelaywr
+
+       ;; transaction counter
+       mov     a,#0ffH
+       mov     dptr,#GPIFTCB0
+       lcall   syncdelaywr
+
+       ;; RDY pins. Not used here.
+        mov     a,#0
+        mov     dptr,#GPIFREADYCFG
+        lcall   syncdelaywr
+
+       ;; drives the output in the IDLE state
+        mov     a,#1
+        mov     dptr,#GPIFIDLECS
+        lcall   syncdelaywr
+
+       ;; direct data transfer from the EP to the GPIF
+       mov     dptr,#EP4FIFOCFG
+       mov     a,#00010000b    ; autoout=1, byte-wide
+       lcall   syncdelaywr
+
+       ;; waveform 0 is used for FIFO out
+       mov     dptr,#GPIFWFSELECT
+       mov     a,#00000000b
+       movx    @dptr,a
+       lcall   syncdelay
+
+       ;; transfer the delay byte from the EP to the waveform
+       mov     dptr,#0e781h    ; EP1 buffer
+       movx    a,@dptr         ; get the delay
+       mov     dptr,#waveform  ; points to the waveform
+       mov     r2,#6           ; fill 6 bytes
+timloop:
+       movx    @dptr,a         ; save timing in a xxx
+       inc     dptr
+       djnz    r2,timloop      ; fill the 6 delay bytes
+
+       ;; load waveform
+        mov     AUTOPTRH2,#0E4H ; XDATA0H
+        lcall   syncdelay
+        mov     AUTOPTRL2,#00H  ; XDATA0L
+        lcall   syncdelay
+
+       mov     dptr,#waveform  ; points to the waveform
+       
+        mov     AUTOPTRSETUP,#7 ; autoinc and enable
+        lcall   syncdelay
+
+        mov     r2,#20H         ; 32 bytes to transfer
+
+wavetr:
+        movx    a,@dptr
+       inc     dptr
+       push    dpl
+       push    dph
+       push    dpl1
+       push    dph1
+        mov     dptr,#XAUTODAT2
+        movx    @dptr,a
+        lcall   syncdelay
+       pop     dph1 
+       pop     dpl1
+       pop     dph 
+       pop     dpl
+        djnz    r2,wavetr
+
+       mov     dptr,#OUTPKTEND
+       mov     a,#084H
+       lcall   syncdelaywr
+       lcall   syncdelaywr
+
+       mov     r0,#PWMFLAG     ; flag for PWM
+       mov     a,#1            ; PWM (for the main loop)
+       mov     @r0,a           ; set it
+
+       ret
+
+
+
+;;; initialise the ports for the AD-converter
+initAD:
+       mov     OEA,#27H        ;PortA0,A1,A2,A5 Outputs
+       mov     IOA,#22H        ;/CS = 1, disable transfers to the converters
+       ret
+
+
+;;; init the timer for the soft counters
+init_timer:
+       ;; init the timer for 2ms sampling rate
+       mov     CKCON,#00000001b; CLKOUT/12 for timer
+       mov     TL0,#010H       ; 16
+       mov     TH0,#0H         ; 256
+       mov     IE,#82H         ; switch on timer interrupt (80H for all IRQs)
+       mov     TMOD,#00000000b ; 13 bit counters
+       setb    TCON.4          ; enable timer 0
+       ret
+
+
+;;; from here it's only IRQ handling...
+       
+;;; A/D-conversion:
+;;; control-byte in a,
+;;; result in r3(low) and r4(high)
+;;; this routine is optimised for speed
+readAD:                                ; mask the control byte
+       anl     a,#01111100b    ; only the channel, gain+pol are left
+       orl     a,#10000001b    ; start bit, external clock
+       ;; set CS to low
+       clr     IOA.1           ; set /CS to zero
+       ;; send the control byte to the AD-converter
+       mov     R2,#8           ; bit-counter
+bitlp: jnb     ACC.7,bitzero   ; jump if Bit7 = 0?
+       setb    IOA.2           ; set the DIN bit
+       sjmp    clock           ; continue with the clock
+bitzero:clr    IOA.2           ; clear the DIN bit
+clock: setb    IOA.0           ; SCLK = 1
+       clr     IOA.0           ; SCLK = 0
+        rl      a               ; next Bit
+        djnz    R2,bitlp
+
+       ;; continue the aquisition (already started)
+       clr     IOA.2           ; clear the DIN bit
+       mov     R2,#5           ; five steps for the aquision
+clockaq:setb   IOA.0           ; SCLK = 1
+       clr     IOA.0           ; SCLK = 0
+        djnz    R2,clockaq     ; loop
+       
+       ;; read highbyte from the A/D-converter
+       ;; and do the conversion
+       mov     r4,#0           ; Highbyte goes into R4
+       mov     R2,#4           ; COUNTER 4 data bits in the MSB
+       mov     r5,#08h         ; create bit-mask
+gethi:                         ; loop get the 8 highest bits from MSB downw
+       setb    IOA.0           ; SCLK = 1
+       clr     IOA.0           ; SCLK = 0
+       mov     a,IOA           ; from port A
+       jnb     ACC.4,zerob     ; the in-bit is zero
+       mov     a,r4            ; get the byte
+       orl     a,r5            ; or the bit to the result
+       mov     r4,a            ; save it again in r4
+zerob: mov     a,r5            ; get r5 in order to shift the mask
+       rr      a               ; rotate right
+       mov     r5,a            ; back to r5
+       djnz    R2,gethi
+       ;; read the lowbyte from the A/D-converter
+       mov     r3,#0           ; Lowbyte goes into R3
+       mov     r2,#8           ; COUNTER 8 data-bits in the LSB
+       mov     r5,#80h         ; create bit-mask
+getlo:                         ; loop get the 8 highest bits from MSB downw
+       setb    IOA.0           ; SCLK = 1
+       clr     IOA.0           ; SCLK = 0
+       mov     a,IOA           ; from port A
+       jnb     ACC.4,zerob2    ; the in-bit is zero
+       mov     a,r3            ; get the result-byte
+       orl     a,r5            ; or the bit to the result
+       mov     r3,a            ; save it again in r4
+zerob2:        mov     a,r5            ; get r5 in order to shift the mask
+       rr      a               ; rotate right
+       mov     r5,a            ; back to r5
+       djnz    R2,getlo
+       setb    IOA.1           ; set /CS to one
+       ;;
+       ret
+       
+
+       
+;;; aquires data from A/D channels and stores them in the EP6 buffer
+conv_ad:
+       mov     AUTOPTRH1,#0F8H ; auto pointer on EP6
+       mov     AUTOPTRL1,#00H
+       mov     AUTOPTRSETUP,#7
+       mov     r0,#CHANNELLIST ; points to the channellist
+
+       mov     a,@r0           ; number of channels
+       mov     r1,a            ; counter
+
+       mov     DPTR,#XAUTODAT1 ; auto pointer
+convloop:
+       inc     r0
+       mov     a,@r0           ; Channel
+       lcall   readAD
+       mov     a,R3            ;
+       movx    @DPTR,A
+       mov     a,R4            ;
+       movx    @DPTR,A
+       djnz    r1,convloop
+
+       ret
+
+
+
+
+;;; initilise the transfer
+;;; It is assumed that the USB interface is in alternate setting 3
+initeps:
+       mov     dptr,#FIFORESET
+       mov     a,#80H          
+       movx    @dptr,a         ; reset all fifos
+       mov     a,#2    
+       movx    @dptr,a         ; 
+       mov     a,#4            
+       movx    @dptr,a         ; 
+       mov     a,#6            
+       movx    @dptr,a         ; 
+       mov     a,#8            
+       movx    @dptr,a         ; 
+       mov     a,#0            
+       movx    @dptr,a         ; normal operat
+       
+       mov     DPTR,#EP2CFG
+       mov     a,#10010010b    ; valid, out, double buff, iso
+       movx    @DPTR,a
+
+       mov     dptr,#EP2FIFOCFG
+       mov     a,#00000000b    ; manual
+       movx    @dptr,a
+
+       mov     dptr,#EP2BCL    ; "arm" it
+       mov     a,#00h
+       movx    @DPTR,a         ; can receive data
+       lcall   syncdelay       ; wait to sync
+       movx    @DPTR,a         ; can receive data
+       lcall   syncdelay       ; wait to sync
+       movx    @DPTR,a         ; can receive data
+       lcall   syncdelay       ; wait to sync
+       
+       mov     DPTR,#EP1OUTCFG
+       mov     a,#10100000b    ; valid
+       movx    @dptr,a
+
+       mov     dptr,#EP1OUTBC  ; "arm" it
+       mov     a,#00h
+       movx    @DPTR,a         ; can receive data
+       lcall   syncdelay       ; wait until we can write again
+       movx    @dptr,a         ; make shure its really empty
+       lcall   syncdelay       ; wait
+
+       mov     DPTR,#EP6CFG    ; ISO data from here to the host
+       mov     a,#11010010b    ; Valid
+       movx    @DPTR,a         ; ISO transfer, double buffering
+
+       mov     DPTR,#EP8CFG    ; EP8
+       mov     a,#11100000b    ; BULK data from here to the host
+       movx    @DPTR,a         ;
+
+       mov     dptr,#EPIE      ; interrupt enable
+       mov     a,#10001000b    ; enable irq for ep1out,8
+       movx    @dptr,a         ; do it
+
+       mov     dptr,#EPIRQ     ; clear IRQs
+       mov     a,#10100000b
+       movx    @dptr,a
+
+       ;; enable interrups
+        mov     DPTR,#USBIE    ; USB int enables register
+        mov     a,#2            ; enables SOF (1ms/125us interrupt)
+        movx    @DPTR,a         ; 
+
+       mov     EIE,#00000001b  ; enable INT2 in the 8051's SFR
+       mov     IE,#80h         ; IE, enable all interrupts
+
+       ret
+
+
+;;; counter
+;;; r0: DIOSTAT
+;;; r1:        counter address
+;;; r2:        up/down-mask
+;;; r3:        reset-mask
+;;; r4:        clock-mask
+counter:       
+       mov     a,IOB           ; actual IOB input state
+       mov     r5,a            ; save in r5
+       anl     a,r3            ; bit mask for reset
+       jz      no_reset        ; reset if one
+       clr     a               ; set counter to zero
+       mov     @r1,a
+       inc     r4
+       mov     @r1,a
+       sjmp    ctr_end
+no_reset:      
+       mov     a,@r0           ; get last state
+       xrl     a,r5            ; has it changed?
+       anl     a,r5            ; is it now on?
+       anl     a,r4            ; mask out the port
+       jz      ctr_end         ; no rising edge
+       mov     a,r5            ; get port B again
+       anl     a,r2            ; test if up or down
+       jnz     ctr_up          ; count up
+       mov     a,@r1
+       dec     a
+       mov     @r1,a
+       cjne    a,#0ffh,ctr_end ; underflow?
+       inc     r1              ; high byte
+       mov     a,@r1
+       dec     a
+       mov     @r1,a
+       sjmp    ctr_end
+ctr_up:                                ; count up
+       mov     a,@r1
+       inc     a
+       mov     @r1,a
+       jnz     ctr_end
+       inc     r1              ; high byte
+       mov     a,@r1
+       inc     a
+       mov     @r1,a
+ctr_end:
+       mov     a,r5
+       mov     @r0,a
+       ret
+
+;;; implements two soft counters with up/down and reset
+timer0_isr:
+       push    dps
+       push    acc
+       push    psw
+       push    00h             ; R0
+       push    01h             ; R1
+       push    02h             ; R2
+       push    03h             ; R3
+       push    04h             ; R4
+       push    05h             ; R5
+               
+       mov     r0,#DIOSTAT0    ; status of port
+       mov     r1,#CTR0        ; address of counter0
+       mov     a,#00000001b    ; bit 0
+       mov     r4,a            ; clock
+       rl      a               ; bit 1
+       mov     r2,a            ; up/down
+       rl      a               ; bit 2
+       mov     r3,a            ; reset mask
+       lcall   counter
+       inc     r0              ; to DISTAT1
+       inc     r1              ; to CTR1
+       inc     r1
+       mov     a,r3
+       rl      a               ; bit 3
+       rl      a               ; bit 4
+       mov     r4,a            ; clock
+       rl      a               ; bit 5
+       mov     r2,a            ; up/down
+       rl      a               ; bit 6
+       mov     r3,a            ; reset
+       lcall   counter
+       
+       pop     05h             ; R5
+       pop     04h             ; R4
+       pop     03h             ; R3
+       pop     02h             ; R2
+       pop     01h             ; R1
+       pop     00h             ; R0
+       pop     psw
+       pop     acc 
+       pop     dps
+
+       reti
+
+;;; interrupt-routine for SOF
+;;; is for full speed
+sof_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     a,EP2468STAT
+       anl     a,#20H          ; full?
+       jnz     epfull          ; EP6-buffer is full
+
+       lcall   conv_ad         ; conversion
+
+       mov     DPTR,#EP6BCH    ; byte count H
+       mov     a,#0            ; is zero
+       lcall   syncdelaywr     ; wait until we can write again
+       
+       mov     DPTR,#EP6BCL    ; byte count L
+       mov     a,#10H          ; is 8x word = 16 bytes
+       lcall   syncdelaywr     ; wait until we can write again
+       
+epfull:
+       ;; do the D/A conversion
+       mov     a,EP2468STAT
+       anl     a,#01H          ; empty
+       jnz     epempty         ; nothing to get
+
+       mov     dptr,#0F000H    ; EP2 fifo buffer
+       lcall   dalo            ; conversion
+
+       mov     dptr,#EP2BCL    ; "arm" it
+       mov     a,#00h
+       lcall   syncdelaywr     ; wait for the rec to sync
+       lcall   syncdelaywr     ; wait for the rec to sync
+
+epempty:       
+       ;; 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,#USBIRQ    ; points to the SOF
+       mov     a,#2            ; clear the SOF
+       movx    @DPTR,a
+
+nosof: 
+       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
+
+
+reset_ep8:
+       ;; erase all data in ep8
+       mov     dptr,#FIFORESET
+       mov     a,#80H          ; NAK
+       lcall   syncdelaywr
+       mov     dptr,#FIFORESET
+       mov     a,#8            ; reset EP8
+       lcall   syncdelaywr
+       mov     dptr,#FIFORESET
+       mov     a,#0            ; normal operation
+       lcall   syncdelaywr
+       ret
+
+
+reset_ep6:
+       ;; throw out old data
+       mov     dptr,#FIFORESET
+       mov     a,#80H          ; NAK
+       lcall   syncdelaywr
+       mov     dptr,#FIFORESET
+       mov     a,#6            ; reset EP6
+       lcall   syncdelaywr
+       mov     dptr,#FIFORESET
+       mov     a,#0            ; normal operation
+       lcall   syncdelaywr
+       ret
+
+;;; interrupt-routine for ep1out
+;;; receives the channel list and other commands
+ep1out_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,#0E780h    ; FIFO buffer of EP1OUT
+       movx    a,@dptr         ; get the first byte
+       mov     r0,#CMD_FLAG    ; pointer to the command byte
+       mov     @r0,a           ; store the command byte for ep8
+
+       mov     dptr,#ep1out_jmp; jump table for the different functions
+       rl      a               ; multiply by 2: sizeof sjmp
+       jmp     @a+dptr         ; jump to the jump table
+       ;; jump table, corresponds to the command bytes defined
+       ;; in usbdux.c
+ep1out_jmp:
+       sjmp    storechannellist; a=0
+       sjmp    single_da       ; a=1
+       sjmp    config_digital_b; a=2
+       sjmp    write_digital_b ; a=3
+       sjmp    storesglchannel ; a=4
+       sjmp    readcounter     ; a=5
+       sjmp    writecounter    ; a=6
+       sjmp    pwm_on          ; a=7
+       sjmp    pwm_off         ; a=8
+
+pwm_on:
+       lcall   startPWM
+       sjmp    over_da
+
+pwm_off:
+       lcall   stopPWM
+       sjmp    over_da
+
+       ;; read the counter
+readcounter:
+       lcall   reset_ep8       ; reset ep8
+       lcall   ep8_ops         ; fill the counter data in there
+       sjmp    over_da         ; jump to the end
+
+       ;; write zeroes to the counters
+writecounter:
+       mov     dptr,#0e781h    ; buffer
+       mov     r0,#CTR0        ; r0 points to counter 0
+       movx    a,@dptr         ; channel number
+       jz      wrctr0          ; first channel
+       mov     r1,a            ; counter
+wrctrl:
+       inc     r0              ; next counter
+       inc     r0              ; next counter
+       djnz    r1,wrctrl       ; advance to the right counter
+wrctr0:
+       inc     dptr            ; get to the value
+       movx    a,@dptr         ; get value
+       mov     @r0,a           ; save in ctr
+       inc     r0              ; next byte
+       inc     dptr
+       movx    a,@dptr         ; get value
+       mov     @r0,a           ; save in ctr
+       sjmp    over_da         ; jump to the end
+
+storesglchannel:
+       mov     r0,#SGLCHANNEL  ; the conversion bytes are now stored in 80h
+       mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
+       movx    a,@dptr         ; 
+       mov     @r0,a
+
+       lcall   reset_ep8       ; reset FIFO
+       ;; Save new A/D data in EP8. This is the first byte
+       ;; the host will read during an INSN. If there are
+       ;; more to come they will be handled by the ISR of
+       ;; ep8.
+       lcall   ep8_ops         ; get A/D data
+               
+       sjmp    over_da
+
+       
+;;; Channellist:
+;;; the first byte is zero:
+;;; we've just received the channel list
+;;; the channel list is stored in the addresses from CHANNELLIST which
+;;; are _only_ reachable by indirect addressing
+storechannellist:
+       mov     r0,#CHANNELLIST ; the conversion bytes are now stored in 80h
+       mov     r2,#9           ; counter
+       mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
+chanlloop:     
+       movx    a,@dptr         ; 
+       mov     @r0,a
+       inc     dptr
+       inc     r0
+       djnz    r2,chanlloop
+
+       lcall   reset_ep6       ; reset FIFO
+       
+       ;; load new A/D data into EP6
+       ;; This must be done. Otherwise the ISR is never called.
+       ;; The ISR is only called when data has _left_ the
+       ;; ep buffer here it has to be refilled.
+       lcall   ep6_arm         ; fill with the first data byte
+       
+       sjmp    over_da
+
+;;; Single DA conversion. The 2 bytes are in the FIFO buffer
+single_da:
+       mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
+       lcall   dalo            ; conversion
+       sjmp    over_da
+
+;;; configure the port B as input or output (bitwise)
+config_digital_b:
+       mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
+       movx    a,@dptr         ; get the second byte
+       mov     OEB,a           ; set the output enable bits
+       sjmp    over_da
+       
+;;; Write one byte to the external digital port B
+;;; and prepare for digital read
+write_digital_b:
+       mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
+       movx    a,@dptr         ; get the second byte
+       mov     OEB,a           ; output enable
+       inc     dptr            ; next byte
+       movx    a,@dptr         ; bits
+       mov     IOB,a           ; send the byte to the I/O port
+
+       lcall   reset_ep8       ; reset FIFO of ep 8
+
+       ;; fill ep8 with new data from port B
+       ;; When the host requests the data it's already there.
+       ;; This must be so. Otherwise the ISR is not called.
+       ;; The ISR is only called when a packet has been delivered
+       ;; to the host. Thus, we need a packet here in the
+       ;; first instance.
+       lcall   ep8_ops         ; get digital data
+
+       ;; 
+       ;; for all commands the same
+over_da:       
+       mov     dptr,#EP1OUTBC
+       mov     a,#00h
+       lcall   syncdelaywr     ; arm
+       lcall   syncdelaywr     ; arm
+       lcall   syncdelaywr     ; arm
+
+       ;; 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,#00001000b    ; clear the ep1outirq
+       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
+
+
+       
+;;; all channels
+dalo:
+       movx    a,@dptr         ; number of channels
+       inc     dptr            ; pointer to the first channel
+       mov     r0,a            ; 4 channels
+nextDA:        
+       movx    a,@dptr         ; get the first low byte
+       mov     r3,a            ; store in r3 (see below)
+       inc     dptr            ; point to the high byte
+       movx    a,@dptr         ; get the high byte
+       mov     r4,a            ; store in r4 (for writeDA)
+       inc     dptr            ; point to the channel number
+       movx    a,@dptr         ; get the channel number
+       inc     dptr            ; get ready for the next channel
+       lcall   writeDA         ; write value to the DAC
+       djnz    r0,nextDA       ; next channel
+       ret
+
+
+
+;;; D/A-conversion:
+;;; control-byte in a,
+;;; value in r3(low) and r4(high)
+writeDA:                       ; mask the control byte
+       anl     a,#11000000b    ; only the channel is left
+       orl     a,#00110000b    ; internal clock, bipolar mode, +/-5V
+       orl     a,r4            ; or the value of R4 to it
+       ;; set CS to low
+       clr     IOA.5           ; set /CS to zero
+       ;; send the first byte to the DA-converter
+       mov     R2,#8           ; bit-counter
+DA1:    jnb     ACC.7,zeroda   ; jump if Bit7 = 0?
+       setb    IOA.2           ; set the DIN bit
+       sjmp    clkda           ; continue with the clock
+zeroda: clr    IOA.2           ; clear the DIN bit
+clkda: setb    IOA.0           ; SCLK = 1
+       clr     IOA.0           ; SCLK = 0
+        rl      a               ; next Bit
+        djnz    R2,DA1
+
+       
+       ;; send the second byte to the DA-converter
+       mov     a,r3            ; low byte
+       mov     R2,#8           ; bit-counter
+DA2:    jnb     ACC.7,zeroda2  ; jump if Bit7 = 0?
+       setb    IOA.2           ; set the DIN bit
+       sjmp    clkda2          ; continue with the clock
+zeroda2:clr    IOA.2           ; clear the DIN bit
+clkda2:        setb    IOA.0           ; SCLK = 1
+       clr     IOA.0           ; SCLK = 0
+        rl      a               ; next Bit
+        djnz    R2,DA2
+       ;; 
+       setb    IOA.5           ; set /CS to one
+       ;; 
+noDA:  ret
+       
+
+
+;;; arm ep6
+ep6_arm:
+       lcall   conv_ad
+       
+       mov     DPTR,#EP6BCH    ; byte count H
+       mov     a,#0            ; is zero
+       lcall   syncdelaywr     ; wait until the length has arrived
+       
+       mov     DPTR,#EP6BCL    ; byte count L
+       mov     a,#10H          ; is one
+       lcall   syncdelaywr     ; wait until the length has been proc
+       ret
+       
+
+
+;;; converts one analog/digital channel and stores it in EP8
+;;; also gets the content of the digital ports B and D depending on
+;;; the COMMAND flag
+ep8_ops:
+       mov     dptr,#0fc01h    ; ep8 fifo buffer
+       clr     a               ; high byte
+       movx    @dptr,a         ; set H=0
+       mov     dptr,#0fc00h    ; low byte
+       mov     r0,#CMD_FLAG
+       mov     a,@r0
+       movx    @dptr,a         ; save command byte
+
+       mov     dptr,#ep8_jmp   ; jump table for the different functions
+       rl      a               ; multiply by 2: sizeof sjmp
+       jmp     @a+dptr         ; jump to the jump table
+       ;; jump table, corresponds to the command bytes defined
+       ;; in usbdux.c
+ep8_jmp:
+       sjmp    ep8_err         ; a=0, err
+       sjmp    ep8_err         ; a=1, err
+       sjmp    ep8_err         ; a=2, err
+       sjmp    ep8_dio         ; a=3, digital read
+       sjmp    ep8_sglchannel  ; a=4, analog A/D
+       sjmp    ep8_readctr     ; a=5, read counter
+       sjmp    ep8_err         ; a=6, write counter
+
+       ;; reads all counters
+ep8_readctr:
+       mov     r0,#CTR0        ; points to counter0
+       mov     dptr,#0fc02h    ; ep8 fifo buffer
+       mov     r1,#8           ; transfer 4 16bit counters
+ep8_ctrlp:
+       mov     a,@r0           ; get the counter
+       movx    @dptr,a         ; save in the fifo buffer
+       inc     r0              ; inc pointer to the counters
+       inc     dptr            ; inc pointer to the fifo buffer
+       djnz    r1,ep8_ctrlp    ; loop until ready
+       
+       sjmp    ep8_send        ; send the data
+       
+       ;; read one A/D channel
+ep8_sglchannel:                
+       mov     r0,#SGLCHANNEL  ; points to the channel
+       mov     a,@r0           ; Ch0
+       
+       lcall   readAD          ; start the conversion
+               
+       mov     DPTR,#0fc02h    ; EP8 FIFO 
+       mov     a,R3            ; get low byte
+       movx    @DPTR,A         ; store in FIFO
+       inc     dptr            ; next fifo entry
+       mov     a,R4            ; get high byte
+       movx    @DPTR,A         ; store in FIFO
+
+       sjmp    ep8_send        ; send the data
+
+       ;; read the digital lines
+ep8_dio:       
+       mov     DPTR,#0fc02h    ; store the contents of port B
+       mov     a,IOB           ; in the next
+       movx    @dptr,a         ; entry of the buffer
+
+       inc     dptr
+       clr     a               ; high byte is zero
+       movx    @dptr,a         ; next byte of the EP
+       
+ep8_send:      
+       mov     DPTR,#EP8BCH    ; byte count H
+       mov     a,#0            ; is zero
+       lcall   syncdelaywr
+       
+       mov     DPTR,#EP8BCL    ; byte count L
+       mov     a,#10H          ; 16 bytes
+       lcall   syncdelaywr     ; send the data over to the host
+
+ep8_err:       
+       ret
+
+
+
+;;; EP8 interrupt: gets one measurement from the AD converter and
+;;; sends it via EP8. The channel # is stored in address 80H.
+;;; It also gets the state of the digital registers B and D.
+ep8_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
+               
+       lcall   ep8_ops
+       
+       ;; 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,#10000000b    ; clear the ep8irq
+       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:
+       movx    @dptr,a
+       lcall   syncdelay
+       ret
+
+
+.End
+
+
diff --git a/usbdux/usbduxfast_firmware.asm b/usbdux/usbduxfast_firmware.asm
new file mode 100644 (file)
index 0000000..0d8e7f8
--- /dev/null
@@ -0,0 +1,547 @@
+;   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
+
+
+
+
+
+
+
+
+
+
+
+