K2LL33D SHELL

 Apache/2.4.7 (Ubuntu)
 Linux sman1baleendah 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64
 uid=33(www-data) gid=33(www-data) groups=33(www-data)
 safemode : OFF
 MySQL: ON | Perl: ON | cURL: OFF | WGet: ON
  >  / lib / firmware / usbdux /
server ip : 172.67.156.115

your ip : 172.70.100.188

H O M E


Filename/lib/firmware/usbdux/usbdux_firmware.asm
Size24.13 kb
Permissionrw-r--r--
Ownerroot : root
Create time27-Apr-2025 09:50
Last modified05-Mar-2014 23:45
Last accessed05-Jul-2025 20:58
Actionsedit | rename | delete | download (gzip)
Viewtext | code | image
; usbdux_firmware.asm
; Copyright (C) 2004,2009 Bernd Porr, [email protected]
; 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 <[email protected]>
; 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