a56: Add missing copyright and licensing information to lex.c and Makefile based...
[linux-libre-firmware.git] / usbdux / usbdux_firmware.asm
1 ;   usbdux_firmware.asm
2 ;   Copyright (C) 2004,2009 Bernd Porr, Bernd.Porr@f2s.com
3 ;   For usbdux.c
4 ;
5 ;   This program is free software; you can redistribute it and/or modify
6 ;   it under the terms of the GNU General Public License as published by
7 ;   the Free Software Foundation; either version 2 of the License, or
8 ;   (at your option) any later version.
9 ;
10 ;   This program is distributed in the hope that it will be useful,
11 ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ;   GNU General Public License for more details.
14 ;
15 ;   You should have received a copy of the GNU General Public License
16 ;   along with this program; if not, write to the Free Software
17 ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 ;
19 ;
20 ; Firmware: usbdux_firmware.asm for usbdux.c
21 ; Description: University of Stirling USB DAQ & INCITE Technology Limited
22 ; Devices: [ITL] USB-DUX (usbdux.o)
23 ; Author: Bernd Porr <Bernd.Porr@f2s.com>
24 ; Updated: 17 Apr 2009
25 ; Status: stable
26 ;
27 ;;;
28 ;;;
29 ;;;
30
31         .inc    fx2-include.asm
32
33         .equ    CHANNELLIST,80h ; channellist in indirect memory
34         
35         .equ    CMD_FLAG,90h    ; flag if next IN transf is DIO
36         .equ    SGLCHANNEL,91h  ; channel for INSN
37         .equ    PWMFLAG,92h     ; PWM
38         
39         .equ    DIOSTAT0,98h    ; last status of the digital port
40         .equ    DIOSTAT1,99h    ; same for the second counter
41         
42         .equ    CTR0,0A0H       ; counter 0
43         .equ    CTR1,0A2H       ; counter 1
44                         
45         .org    0000h           ; after reset the processor starts here
46         ljmp    main            ; jump to the main loop
47
48         .org    000bh           ; timer 0 irq
49         ljmp    timer0_isr
50
51         .org    0043h           ; the IRQ2-vector
52         ljmp    jmptbl          ; irq service-routine
53         
54         .org    0100h           ; start of the jump table
55
56 jmptbl: ljmp    sudav_isr
57         nop
58         ljmp    sof_isr
59         nop
60         ljmp    sutok_isr
61         nop
62         ljmp    suspend_isr
63         nop
64         ljmp    usbreset_isr
65         nop
66         ljmp    hispeed_isr
67         nop
68         ljmp    ep0ack_isr
69         nop
70         ljmp    spare_isr
71         nop
72         ljmp    ep0in_isr
73         nop
74         ljmp    ep0out_isr
75         nop
76         ljmp    ep1in_isr
77         nop
78         ljmp    ep1out_isr
79         nop
80         ljmp    ep2_isr
81         nop
82         ljmp    ep4_isr
83         nop
84         ljmp    ep6_isr
85         nop
86         ljmp    ep8_isr
87         nop
88         ljmp    ibn_isr
89         nop
90         ljmp    spare_isr
91         nop
92         ljmp    ep0ping_isr
93         nop
94         ljmp    ep1ping_isr
95         nop
96         ljmp    ep2ping_isr
97         nop
98         ljmp    ep4ping_isr
99         nop
100         ljmp    ep6ping_isr
101         nop
102         ljmp    ep8ping_isr
103         nop
104         ljmp    errlimit_isr
105         nop
106         ljmp    spare_isr
107         nop
108         ljmp    spare_isr
109         nop
110         ljmp    spare_isr
111         nop
112         ljmp    ep2isoerr_isr
113         nop
114         ljmp    ep4isoerr_isr
115         nop
116         ljmp    ep6isoerr_isr
117         nop
118         ljmp    ep8isoerr_isr
119
120         
121         ;; dummy isr
122 sudav_isr:      
123 sutok_isr:      
124 suspend_isr:    
125 usbreset_isr:   
126 hispeed_isr:    
127 ep0ack_isr:     
128 spare_isr:      
129 ep0in_isr:      
130 ep0out_isr:     
131 ep1in_isr:      
132 ibn_isr:        
133 ep0ping_isr:    
134 ep1ping_isr:    
135 ep2ping_isr:    
136 ep4ping_isr:    
137 ep6ping_isr:    
138 ep8ping_isr:    
139 errlimit_isr:   
140 ep2isoerr_isr:  
141 ep4isoerr_isr:  
142 ep6isoerr_isr:  
143 ep8isoerr_isr:
144 ep6_isr:
145 ep2_isr:
146 ep4_isr:        
147
148         push    dps
149         push    dpl
150         push    dph
151         push    dpl1
152         push    dph1
153         push    acc
154         push    psw
155
156         ;; clear the USB2 irq bit and return
157         mov     a,EXIF
158         clr     acc.4
159         mov     EXIF,a
160
161         pop     psw
162         pop     acc 
163         pop     dph1 
164         pop     dpl1
165         pop     dph 
166         pop     dpl 
167         pop     dps
168         
169         reti
170
171                 
172 ;;; main program
173 ;;; basically only initialises the processor and
174 ;;; then engages in an endless loop
175 main:
176         mov     DPTR,#CPUCS     ; CPU control register
177         mov     a,#00010000b    ; 48Mhz
178         lcall   syncdelaywr
179
180         mov     dptr,#REVCTL
181         mov     a,#00000011b    ; allows skip
182         lcall   syncdelaywr
183
184         mov     IP,#0           ; all std 8051 int have low priority
185         mov     EIP,#0FFH       ; all FX2 interrupts have high priority
186         
187         mov     dptr,#INTSETUP  ; IRQ setup register
188         mov     a,#08h          ; enable autovector
189         lcall   syncdelaywr
190
191         lcall   initAD          ; init the ports to the converters
192
193         lcall   initeps         ; init the isochronous data-transfer
194
195         lcall   init_timer
196         
197 mloop2: nop
198
199 ;;; pwm
200         mov     r0,#PWMFLAG     ; pwm on?
201         mov     a,@r0           ; get info
202         jz      mloop2          ; it's off
203
204         mov     a,GPIFTRIG      ; GPIF status
205         anl     a,#80h          ; done bit
206         jz      mloop2          ; GPIF still busy
207
208         mov     a,#01h          ; WR,EP4, 01 = EP4
209         mov     GPIFTRIG,a      ; restart it
210
211         sjmp    mloop2          ; loop for ever
212
213
214 ;;; GPIF waveform for PWM
215 waveform:
216         ;;      0     1     2     3     4     5     6     7(not used)
217         ;; len (gives 50.007Hz)
218         .db     195,  195,  195,  195,  195,  195,  1,    1
219
220         ;; opcode
221         .db     002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
222         
223         ;; out
224         .db     0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
225
226         ;; log
227         .db     000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
228
229
230 stopPWM:
231         mov     r0,#PWMFLAG     ; flag for PWM
232         mov     a,#0            ; PWM (for the main loop)
233         mov     @r0,a           ; set it
234
235         mov     dptr,#IFCONFIG  ; switch off GPIF
236         mov     a,#10000000b    ; gpif, 30MHz, internal IFCLK
237         lcall   syncdelaywr
238         ret
239         
240
241 ;;; init PWM
242 startPWM:
243         mov     dptr,#IFCONFIG  ; switch on IFCLK signal
244         mov     a,#10000010b    ; gpif, 30MHz, internal IFCLK
245         lcall   syncdelaywr
246
247         mov     OEB,0FFH        ; output to port B
248
249         mov     DPTR,#EP4CFG
250         mov     a,#10100000b    ; valid, out, bulk
251         movx    @DPTR,a
252
253         ;; reset the endpoint
254         mov     dptr,#FIFORESET
255         mov     a,#80h          ; NAK
256         lcall   syncdelaywr
257         mov     a,#84h          ; reset EP4 + NAK
258         lcall   syncdelaywr
259         mov     a,#0            ; normal op
260         lcall   syncdelaywr
261
262         mov     dptr,#EP4BCL
263         mov     a,#0H           ; discard packets
264         lcall   syncdelaywr     ; empty FIFO buffer
265         lcall   syncdelaywr     ; empty FIFO buffer
266
267         ;; aborts all transfers by the GPIF
268         mov     dptr,#GPIFABORT
269         mov     a,#0ffh         ; abort all transfers
270         lcall   syncdelaywr
271
272         ;; wait for GPIF to finish
273 wait_f_abort:
274         mov     a,GPIFTRIG      ; GPIF status
275         anl     a,#80h          ; done bit
276         jz      wait_f_abort    ; GPIF busy
277
278         mov     dptr,#GPIFCTLCFG
279         mov     a,#10000000b    ; tri state for CTRL
280         lcall   syncdelaywr
281
282         mov     dptr,#GPIFIDLECTL
283         mov     a,#11110000b    ; all CTL outputs low
284         lcall   syncdelaywr
285
286         ;; abort if FIFO is empty
287         mov     a,#00000001b    ; abort if empty
288         mov     dptr,#EP4GPIFFLGSEL
289         lcall   syncdelaywr
290
291         ;; 
292         mov     a,#00000001b    ; stop if GPIF flg
293         mov     dptr,#EP4GPIFPFSTOP
294         lcall   syncdelaywr
295
296         ;; transaction counter
297         mov     a,#0ffH
298         mov     dptr,#GPIFTCB3
299         lcall   syncdelaywr
300
301         ;; transaction counter
302         mov     a,#0ffH
303         mov     dptr,#GPIFTCB2
304         lcall   syncdelaywr
305
306         ;; transaction counter
307         mov     a,#0ffH         ; 512 bytes
308         mov     dptr,#GPIFTCB1
309         lcall   syncdelaywr
310
311         ;; transaction counter
312         mov     a,#0ffH
313         mov     dptr,#GPIFTCB0
314         lcall   syncdelaywr
315
316         ;; RDY pins. Not used here.
317         mov     a,#0
318         mov     dptr,#GPIFREADYCFG
319         lcall   syncdelaywr
320
321         ;; drives the output in the IDLE state
322         mov     a,#1
323         mov     dptr,#GPIFIDLECS
324         lcall   syncdelaywr
325
326         ;; direct data transfer from the EP to the GPIF
327         mov     dptr,#EP4FIFOCFG
328         mov     a,#00010000b    ; autoout=1, byte-wide
329         lcall   syncdelaywr
330
331         ;; waveform 0 is used for FIFO out
332         mov     dptr,#GPIFWFSELECT
333         mov     a,#00000000b
334         movx    @dptr,a
335         lcall   syncdelay
336
337         ;; transfer the delay byte from the EP to the waveform
338         mov     dptr,#0e781h    ; EP1 buffer
339         movx    a,@dptr         ; get the delay
340         mov     dptr,#waveform  ; points to the waveform
341         mov     r2,#6           ; fill 6 bytes
342 timloop:
343         movx    @dptr,a         ; save timing in a xxx
344         inc     dptr
345         djnz    r2,timloop      ; fill the 6 delay bytes
346
347         ;; load waveform
348         mov     AUTOPTRH2,#0E4H ; XDATA0H
349         lcall   syncdelay
350         mov     AUTOPTRL2,#00H  ; XDATA0L
351         lcall   syncdelay
352
353         mov     dptr,#waveform  ; points to the waveform
354         
355         mov     AUTOPTRSETUP,#7 ; autoinc and enable
356         lcall   syncdelay
357
358         mov     r2,#20H         ; 32 bytes to transfer
359
360 wavetr:
361         movx    a,@dptr
362         inc     dptr
363         push    dpl
364         push    dph
365         push    dpl1
366         push    dph1
367         mov     dptr,#XAUTODAT2
368         movx    @dptr,a
369         lcall   syncdelay
370         pop     dph1 
371         pop     dpl1
372         pop     dph 
373         pop     dpl
374         djnz    r2,wavetr
375
376         mov     dptr,#OUTPKTEND
377         mov     a,#084H
378         lcall   syncdelaywr
379         lcall   syncdelaywr
380
381         mov     r0,#PWMFLAG     ; flag for PWM
382         mov     a,#1            ; PWM (for the main loop)
383         mov     @r0,a           ; set it
384
385         ret
386
387
388
389 ;;; initialise the ports for the AD-converter
390 initAD:
391         mov     OEA,#27H        ;PortA0,A1,A2,A5 Outputs
392         mov     IOA,#22H        ;/CS = 1, disable transfers to the converters
393         ret
394
395
396 ;;; init the timer for the soft counters
397 init_timer:
398         ;; init the timer for 2ms sampling rate
399         mov     CKCON,#00000001b; CLKOUT/12 for timer
400         mov     TL0,#010H       ; 16
401         mov     TH0,#0H         ; 256
402         mov     IE,#82H         ; switch on timer interrupt (80H for all IRQs)
403         mov     TMOD,#00000000b ; 13 bit counters
404         setb    TCON.4          ; enable timer 0
405         ret
406
407
408 ;;; from here it's only IRQ handling...
409         
410 ;;; A/D-conversion:
411 ;;; control-byte in a,
412 ;;; result in r3(low) and r4(high)
413 ;;; this routine is optimised for speed
414 readAD:                         ; mask the control byte
415         anl     a,#01111100b    ; only the channel, gain+pol are left
416         orl     a,#10000001b    ; start bit, external clock
417         ;; set CS to low
418         clr     IOA.1           ; set /CS to zero
419         ;; send the control byte to the AD-converter
420         mov     R2,#8           ; bit-counter
421 bitlp:  jnb     ACC.7,bitzero   ; jump if Bit7 = 0?
422         setb    IOA.2           ; set the DIN bit
423         sjmp    clock           ; continue with the clock
424 bitzero:clr     IOA.2           ; clear the DIN bit
425 clock:  setb    IOA.0           ; SCLK = 1
426         clr     IOA.0           ; SCLK = 0
427         rl      a               ; next Bit
428         djnz    R2,bitlp
429
430         ;; continue the aquisition (already started)
431         clr     IOA.2           ; clear the DIN bit
432         mov     R2,#5           ; five steps for the aquision
433 clockaq:setb    IOA.0           ; SCLK = 1
434         clr     IOA.0           ; SCLK = 0
435         djnz    R2,clockaq      ; loop
436         
437         ;; read highbyte from the A/D-converter
438         ;; and do the conversion
439         mov     r4,#0           ; Highbyte goes into R4
440         mov     R2,#4           ; COUNTER 4 data bits in the MSB
441         mov     r5,#08h         ; create bit-mask
442 gethi:                          ; loop get the 8 highest bits from MSB downw
443         setb    IOA.0           ; SCLK = 1
444         clr     IOA.0           ; SCLK = 0
445         mov     a,IOA           ; from port A
446         jnb     ACC.4,zerob     ; the in-bit is zero
447         mov     a,r4            ; get the byte
448         orl     a,r5            ; or the bit to the result
449         mov     r4,a            ; save it again in r4
450 zerob:  mov     a,r5            ; get r5 in order to shift the mask
451         rr      a               ; rotate right
452         mov     r5,a            ; back to r5
453         djnz    R2,gethi
454         ;; read the lowbyte from the A/D-converter
455         mov     r3,#0           ; Lowbyte goes into R3
456         mov     r2,#8           ; COUNTER 8 data-bits in the LSB
457         mov     r5,#80h         ; create bit-mask
458 getlo:                          ; loop get the 8 highest bits from MSB downw
459         setb    IOA.0           ; SCLK = 1
460         clr     IOA.0           ; SCLK = 0
461         mov     a,IOA           ; from port A
462         jnb     ACC.4,zerob2    ; the in-bit is zero
463         mov     a,r3            ; get the result-byte
464         orl     a,r5            ; or the bit to the result
465         mov     r3,a            ; save it again in r4
466 zerob2: mov     a,r5            ; get r5 in order to shift the mask
467         rr      a               ; rotate right
468         mov     r5,a            ; back to r5
469         djnz    R2,getlo
470         setb    IOA.1           ; set /CS to one
471         ;;
472         ret
473         
474
475         
476 ;;; aquires data from A/D channels and stores them in the EP6 buffer
477 conv_ad:
478         mov     AUTOPTRH1,#0F8H ; auto pointer on EP6
479         mov     AUTOPTRL1,#00H
480         mov     AUTOPTRSETUP,#7
481         mov     r0,#CHANNELLIST ; points to the channellist
482
483         mov     a,@r0           ; number of channels
484         mov     r1,a            ; counter
485
486         mov     DPTR,#XAUTODAT1 ; auto pointer
487 convloop:
488         inc     r0
489         mov     a,@r0           ; Channel
490         lcall   readAD
491         mov     a,R3            ;
492         movx    @DPTR,A
493         mov     a,R4            ;
494         movx    @DPTR,A
495         djnz    r1,convloop
496
497         ret
498
499
500
501
502 ;;; initilise the transfer
503 ;;; It is assumed that the USB interface is in alternate setting 3
504 initeps:
505         mov     dptr,#FIFORESET
506         mov     a,#80H          
507         movx    @dptr,a         ; reset all fifos
508         mov     a,#2    
509         movx    @dptr,a         ; 
510         mov     a,#4            
511         movx    @dptr,a         ; 
512         mov     a,#6            
513         movx    @dptr,a         ; 
514         mov     a,#8            
515         movx    @dptr,a         ; 
516         mov     a,#0            
517         movx    @dptr,a         ; normal operat
518         
519         mov     DPTR,#EP2CFG
520         mov     a,#10010010b    ; valid, out, double buff, iso
521         movx    @DPTR,a
522
523         mov     dptr,#EP2FIFOCFG
524         mov     a,#00000000b    ; manual
525         movx    @dptr,a
526
527         mov     dptr,#EP2BCL    ; "arm" it
528         mov     a,#00h
529         movx    @DPTR,a         ; can receive data
530         lcall   syncdelay       ; wait to sync
531         movx    @DPTR,a         ; can receive data
532         lcall   syncdelay       ; wait to sync
533         movx    @DPTR,a         ; can receive data
534         lcall   syncdelay       ; wait to sync
535         
536         mov     DPTR,#EP1OUTCFG
537         mov     a,#10100000b    ; valid
538         movx    @dptr,a
539
540         mov     dptr,#EP1OUTBC  ; "arm" it
541         mov     a,#00h
542         movx    @DPTR,a         ; can receive data
543         lcall   syncdelay       ; wait until we can write again
544         movx    @dptr,a         ; make shure its really empty
545         lcall   syncdelay       ; wait
546
547         mov     DPTR,#EP6CFG    ; ISO data from here to the host
548         mov     a,#11010010b    ; Valid
549         movx    @DPTR,a         ; ISO transfer, double buffering
550
551         mov     DPTR,#EP8CFG    ; EP8
552         mov     a,#11100000b    ; BULK data from here to the host
553         movx    @DPTR,a         ;
554
555         mov     dptr,#EPIE      ; interrupt enable
556         mov     a,#10001000b    ; enable irq for ep1out,8
557         movx    @dptr,a         ; do it
558
559         mov     dptr,#EPIRQ     ; clear IRQs
560         mov     a,#10100000b
561         movx    @dptr,a
562
563         ;; enable interrups
564         mov     DPTR,#USBIE     ; USB int enables register
565         mov     a,#2            ; enables SOF (1ms/125us interrupt)
566         movx    @DPTR,a         ; 
567
568         mov     EIE,#00000001b  ; enable INT2 in the 8051's SFR
569         mov     IE,#80h         ; IE, enable all interrupts
570
571         ret
572
573
574 ;;; counter
575 ;;; r0: DIOSTAT
576 ;;; r1: counter address
577 ;;; r2: up/down-mask
578 ;;; r3: reset-mask
579 ;;; r4: clock-mask
580 counter:        
581         mov     a,IOB           ; actual IOB input state
582         mov     r5,a            ; save in r5
583         anl     a,r3            ; bit mask for reset
584         jz      no_reset        ; reset if one
585         clr     a               ; set counter to zero
586         mov     @r1,a
587         inc     r4
588         mov     @r1,a
589         sjmp    ctr_end
590 no_reset:       
591         mov     a,@r0           ; get last state
592         xrl     a,r5            ; has it changed?
593         anl     a,r5            ; is it now on?
594         anl     a,r4            ; mask out the port
595         jz      ctr_end         ; no rising edge
596         mov     a,r5            ; get port B again
597         anl     a,r2            ; test if up or down
598         jnz     ctr_up          ; count up
599         mov     a,@r1
600         dec     a
601         mov     @r1,a
602         cjne    a,#0ffh,ctr_end ; underflow?
603         inc     r1              ; high byte
604         mov     a,@r1
605         dec     a
606         mov     @r1,a
607         sjmp    ctr_end
608 ctr_up:                         ; count up
609         mov     a,@r1
610         inc     a
611         mov     @r1,a
612         jnz     ctr_end
613         inc     r1              ; high byte
614         mov     a,@r1
615         inc     a
616         mov     @r1,a
617 ctr_end:
618         mov     a,r5
619         mov     @r0,a
620         ret
621
622 ;;; implements two soft counters with up/down and reset
623 timer0_isr:
624         push    dps
625         push    acc
626         push    psw
627         push    00h             ; R0
628         push    01h             ; R1
629         push    02h             ; R2
630         push    03h             ; R3
631         push    04h             ; R4
632         push    05h             ; R5
633                 
634         mov     r0,#DIOSTAT0    ; status of port
635         mov     r1,#CTR0        ; address of counter0
636         mov     a,#00000001b    ; bit 0
637         mov     r4,a            ; clock
638         rl      a               ; bit 1
639         mov     r2,a            ; up/down
640         rl      a               ; bit 2
641         mov     r3,a            ; reset mask
642         lcall   counter
643         inc     r0              ; to DISTAT1
644         inc     r1              ; to CTR1
645         inc     r1
646         mov     a,r3
647         rl      a               ; bit 3
648         rl      a               ; bit 4
649         mov     r4,a            ; clock
650         rl      a               ; bit 5
651         mov     r2,a            ; up/down
652         rl      a               ; bit 6
653         mov     r3,a            ; reset
654         lcall   counter
655         
656         pop     05h             ; R5
657         pop     04h             ; R4
658         pop     03h             ; R3
659         pop     02h             ; R2
660         pop     01h             ; R1
661         pop     00h             ; R0
662         pop     psw
663         pop     acc 
664         pop     dps
665
666         reti
667
668 ;;; interrupt-routine for SOF
669 ;;; is for full speed
670 sof_isr:
671         push    dps
672         push    dpl
673         push    dph
674         push    dpl1
675         push    dph1
676         push    acc
677         push    psw
678         push    00h             ; R0
679         push    01h             ; R1
680         push    02h             ; R2
681         push    03h             ; R3
682         push    04h             ; R4
683         push    05h             ; R5
684         push    06h             ; R6
685         push    07h             ; R7
686                 
687         mov     a,EP2468STAT
688         anl     a,#20H          ; full?
689         jnz     epfull          ; EP6-buffer is full
690
691         lcall   conv_ad         ; conversion
692
693         mov     DPTR,#EP6BCH    ; byte count H
694         mov     a,#0            ; is zero
695         lcall   syncdelaywr     ; wait until we can write again
696         
697         mov     DPTR,#EP6BCL    ; byte count L
698         mov     a,#10H          ; is 8x word = 16 bytes
699         lcall   syncdelaywr     ; wait until we can write again
700         
701 epfull:
702         ;; do the D/A conversion
703         mov     a,EP2468STAT
704         anl     a,#01H          ; empty
705         jnz     epempty         ; nothing to get
706
707         mov     dptr,#0F000H    ; EP2 fifo buffer
708         lcall   dalo            ; conversion
709
710         mov     dptr,#EP2BCL    ; "arm" it
711         mov     a,#00h
712         lcall   syncdelaywr     ; wait for the rec to sync
713         lcall   syncdelaywr     ; wait for the rec to sync
714
715 epempty:        
716         ;; clear INT2
717         mov     a,EXIF          ; FIRST clear the USB (INT2) interrupt request
718         clr     acc.4
719         mov     EXIF,a          ; Note: EXIF reg is not 8051 bit-addressable
720         
721         mov     DPTR,#USBIRQ    ; points to the SOF
722         mov     a,#2            ; clear the SOF
723         movx    @DPTR,a
724
725 nosof:  
726         pop     07h
727         pop     06h
728         pop     05h
729         pop     04h             ; R4
730         pop     03h             ; R3
731         pop     02h             ; R2
732         pop     01h             ; R1
733         pop     00h             ; R0
734         pop     psw
735         pop     acc 
736         pop     dph1 
737         pop     dpl1
738         pop     dph 
739         pop     dpl 
740         pop     dps
741         reti
742
743
744 reset_ep8:
745         ;; erase all data in ep8
746         mov     dptr,#FIFORESET
747         mov     a,#80H          ; NAK
748         lcall   syncdelaywr
749         mov     dptr,#FIFORESET
750         mov     a,#8            ; reset EP8
751         lcall   syncdelaywr
752         mov     dptr,#FIFORESET
753         mov     a,#0            ; normal operation
754         lcall   syncdelaywr
755         ret
756
757
758 reset_ep6:
759         ;; throw out old data
760         mov     dptr,#FIFORESET
761         mov     a,#80H          ; NAK
762         lcall   syncdelaywr
763         mov     dptr,#FIFORESET
764         mov     a,#6            ; reset EP6
765         lcall   syncdelaywr
766         mov     dptr,#FIFORESET
767         mov     a,#0            ; normal operation
768         lcall   syncdelaywr
769         ret
770
771 ;;; interrupt-routine for ep1out
772 ;;; receives the channel list and other commands
773 ep1out_isr:
774         push    dps
775         push    dpl
776         push    dph
777         push    dpl1
778         push    dph1
779         push    acc
780         push    psw
781         push    00h             ; R0
782         push    01h             ; R1
783         push    02h             ; R2
784         push    03h             ; R3
785         push    04h             ; R4
786         push    05h             ; R5
787         push    06h             ; R6
788         push    07h             ; R7
789                 
790         mov     dptr,#0E780h    ; FIFO buffer of EP1OUT
791         movx    a,@dptr         ; get the first byte
792         mov     r0,#CMD_FLAG    ; pointer to the command byte
793         mov     @r0,a           ; store the command byte for ep8
794
795         mov     dptr,#ep1out_jmp; jump table for the different functions
796         rl      a               ; multiply by 2: sizeof sjmp
797         jmp     @a+dptr         ; jump to the jump table
798         ;; jump table, corresponds to the command bytes defined
799         ;; in usbdux.c
800 ep1out_jmp:
801         sjmp    storechannellist; a=0
802         sjmp    single_da       ; a=1
803         sjmp    config_digital_b; a=2
804         sjmp    write_digital_b ; a=3
805         sjmp    storesglchannel ; a=4
806         sjmp    readcounter     ; a=5
807         sjmp    writecounter    ; a=6
808         sjmp    pwm_on          ; a=7
809         sjmp    pwm_off         ; a=8
810
811 pwm_on:
812         lcall   startPWM
813         sjmp    over_da
814
815 pwm_off:
816         lcall   stopPWM
817         sjmp    over_da
818
819         ;; read the counter
820 readcounter:
821         lcall   reset_ep8       ; reset ep8
822         lcall   ep8_ops         ; fill the counter data in there
823         sjmp    over_da         ; jump to the end
824
825         ;; write zeroes to the counters
826 writecounter:
827         mov     dptr,#0e781h    ; buffer
828         mov     r0,#CTR0        ; r0 points to counter 0
829         movx    a,@dptr         ; channel number
830         jz      wrctr0          ; first channel
831         mov     r1,a            ; counter
832 wrctrl:
833         inc     r0              ; next counter
834         inc     r0              ; next counter
835         djnz    r1,wrctrl       ; advance to the right counter
836 wrctr0:
837         inc     dptr            ; get to the value
838         movx    a,@dptr         ; get value
839         mov     @r0,a           ; save in ctr
840         inc     r0              ; next byte
841         inc     dptr
842         movx    a,@dptr         ; get value
843         mov     @r0,a           ; save in ctr
844         sjmp    over_da         ; jump to the end
845
846 storesglchannel:
847         mov     r0,#SGLCHANNEL  ; the conversion bytes are now stored in 80h
848         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
849         movx    a,@dptr         ; 
850         mov     @r0,a
851
852         lcall   reset_ep8       ; reset FIFO
853         ;; Save new A/D data in EP8. This is the first byte
854         ;; the host will read during an INSN. If there are
855         ;; more to come they will be handled by the ISR of
856         ;; ep8.
857         lcall   ep8_ops         ; get A/D data
858                 
859         sjmp    over_da
860
861         
862 ;;; Channellist:
863 ;;; the first byte is zero:
864 ;;; we've just received the channel list
865 ;;; the channel list is stored in the addresses from CHANNELLIST which
866 ;;; are _only_ reachable by indirect addressing
867 storechannellist:
868         mov     r0,#CHANNELLIST ; the conversion bytes are now stored in 80h
869         mov     r2,#9           ; counter
870         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
871 chanlloop:      
872         movx    a,@dptr         ; 
873         mov     @r0,a
874         inc     dptr
875         inc     r0
876         djnz    r2,chanlloop
877
878         lcall   reset_ep6       ; reset FIFO
879         
880         ;; load new A/D data into EP6
881         ;; This must be done. Otherwise the ISR is never called.
882         ;; The ISR is only called when data has _left_ the
883         ;; ep buffer here it has to be refilled.
884         lcall   ep6_arm         ; fill with the first data byte
885         
886         sjmp    over_da
887
888 ;;; Single DA conversion. The 2 bytes are in the FIFO buffer
889 single_da:
890         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
891         lcall   dalo            ; conversion
892         sjmp    over_da
893
894 ;;; configure the port B as input or output (bitwise)
895 config_digital_b:
896         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
897         movx    a,@dptr         ; get the second byte
898         mov     OEB,a           ; set the output enable bits
899         sjmp    over_da
900         
901 ;;; Write one byte to the external digital port B
902 ;;; and prepare for digital read
903 write_digital_b:
904         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
905         movx    a,@dptr         ; get the second byte
906         mov     OEB,a           ; output enable
907         inc     dptr            ; next byte
908         movx    a,@dptr         ; bits
909         mov     IOB,a           ; send the byte to the I/O port
910
911         lcall   reset_ep8       ; reset FIFO of ep 8
912
913         ;; fill ep8 with new data from port B
914         ;; When the host requests the data it's already there.
915         ;; This must be so. Otherwise the ISR is not called.
916         ;; The ISR is only called when a packet has been delivered
917         ;; to the host. Thus, we need a packet here in the
918         ;; first instance.
919         lcall   ep8_ops         ; get digital data
920
921         ;; 
922         ;; for all commands the same
923 over_da:        
924         mov     dptr,#EP1OUTBC
925         mov     a,#00h
926         lcall   syncdelaywr     ; arm
927         lcall   syncdelaywr     ; arm
928         lcall   syncdelaywr     ; arm
929
930         ;; clear INT2
931         mov     a,EXIF          ; FIRST clear the USB (INT2) interrupt request
932         clr     acc.4
933         mov     EXIF,a          ; Note: EXIF reg is not 8051 bit-addressable
934
935         mov     DPTR,#EPIRQ     ; 
936         mov     a,#00001000b    ; clear the ep1outirq
937         movx    @DPTR,a
938
939         pop     07h
940         pop     06h
941         pop     05h
942         pop     04h             ; R4
943         pop     03h             ; R3
944         pop     02h             ; R2
945         pop     01h             ; R1
946         pop     00h             ; R0
947         pop     psw
948         pop     acc 
949         pop     dph1 
950         pop     dpl1
951         pop     dph 
952         pop     dpl 
953         pop     dps
954         reti
955
956
957         
958 ;;; all channels
959 dalo:
960         movx    a,@dptr         ; number of channels
961         inc     dptr            ; pointer to the first channel
962         mov     r0,a            ; 4 channels
963 nextDA: 
964         movx    a,@dptr         ; get the first low byte
965         mov     r3,a            ; store in r3 (see below)
966         inc     dptr            ; point to the high byte
967         movx    a,@dptr         ; get the high byte
968         mov     r4,a            ; store in r4 (for writeDA)
969         inc     dptr            ; point to the channel number
970         movx    a,@dptr         ; get the channel number
971         inc     dptr            ; get ready for the next channel
972         lcall   writeDA         ; write value to the DAC
973         djnz    r0,nextDA       ; next channel
974         ret
975
976
977
978 ;;; D/A-conversion:
979 ;;; control-byte in a,
980 ;;; value in r3(low) and r4(high)
981 writeDA:                        ; mask the control byte
982         anl     a,#11000000b    ; only the channel is left
983         orl     a,#00110000b    ; internal clock, bipolar mode, +/-5V
984         orl     a,r4            ; or the value of R4 to it
985         ;; set CS to low
986         clr     IOA.5           ; set /CS to zero
987         ;; send the first byte to the DA-converter
988         mov     R2,#8           ; bit-counter
989 DA1:    jnb     ACC.7,zeroda    ; jump if Bit7 = 0?
990         setb    IOA.2           ; set the DIN bit
991         sjmp    clkda           ; continue with the clock
992 zeroda: clr     IOA.2           ; clear the DIN bit
993 clkda:  setb    IOA.0           ; SCLK = 1
994         clr     IOA.0           ; SCLK = 0
995         rl      a               ; next Bit
996         djnz    R2,DA1
997
998         
999         ;; send the second byte to the DA-converter
1000         mov     a,r3            ; low byte
1001         mov     R2,#8           ; bit-counter
1002 DA2:    jnb     ACC.7,zeroda2   ; jump if Bit7 = 0?
1003         setb    IOA.2           ; set the DIN bit
1004         sjmp    clkda2          ; continue with the clock
1005 zeroda2:clr     IOA.2           ; clear the DIN bit
1006 clkda2: setb    IOA.0           ; SCLK = 1
1007         clr     IOA.0           ; SCLK = 0
1008         rl      a               ; next Bit
1009         djnz    R2,DA2
1010         ;; 
1011         setb    IOA.5           ; set /CS to one
1012         ;; 
1013 noDA:   ret
1014         
1015
1016
1017 ;;; arm ep6
1018 ep6_arm:
1019         lcall   conv_ad
1020         
1021         mov     DPTR,#EP6BCH    ; byte count H
1022         mov     a,#0            ; is zero
1023         lcall   syncdelaywr     ; wait until the length has arrived
1024         
1025         mov     DPTR,#EP6BCL    ; byte count L
1026         mov     a,#10H          ; is one
1027         lcall   syncdelaywr     ; wait until the length has been proc
1028         ret
1029         
1030
1031
1032 ;;; converts one analog/digital channel and stores it in EP8
1033 ;;; also gets the content of the digital ports B and D depending on
1034 ;;; the COMMAND flag
1035 ep8_ops:
1036         mov     dptr,#0fc01h    ; ep8 fifo buffer
1037         clr     a               ; high byte
1038         movx    @dptr,a         ; set H=0
1039         mov     dptr,#0fc00h    ; low byte
1040         mov     r0,#CMD_FLAG
1041         mov     a,@r0
1042         movx    @dptr,a         ; save command byte
1043
1044         mov     dptr,#ep8_jmp   ; jump table for the different functions
1045         rl      a               ; multiply by 2: sizeof sjmp
1046         jmp     @a+dptr         ; jump to the jump table
1047         ;; jump table, corresponds to the command bytes defined
1048         ;; in usbdux.c
1049 ep8_jmp:
1050         sjmp    ep8_err         ; a=0, err
1051         sjmp    ep8_err         ; a=1, err
1052         sjmp    ep8_err         ; a=2, err
1053         sjmp    ep8_dio         ; a=3, digital read
1054         sjmp    ep8_sglchannel  ; a=4, analog A/D
1055         sjmp    ep8_readctr     ; a=5, read counter
1056         sjmp    ep8_err         ; a=6, write counter
1057
1058         ;; reads all counters
1059 ep8_readctr:
1060         mov     r0,#CTR0        ; points to counter0
1061         mov     dptr,#0fc02h    ; ep8 fifo buffer
1062         mov     r1,#8           ; transfer 4 16bit counters
1063 ep8_ctrlp:
1064         mov     a,@r0           ; get the counter
1065         movx    @dptr,a         ; save in the fifo buffer
1066         inc     r0              ; inc pointer to the counters
1067         inc     dptr            ; inc pointer to the fifo buffer
1068         djnz    r1,ep8_ctrlp    ; loop until ready
1069         
1070         sjmp    ep8_send        ; send the data
1071         
1072         ;; read one A/D channel
1073 ep8_sglchannel:         
1074         mov     r0,#SGLCHANNEL  ; points to the channel
1075         mov     a,@r0           ; Ch0
1076         
1077         lcall   readAD          ; start the conversion
1078                 
1079         mov     DPTR,#0fc02h    ; EP8 FIFO 
1080         mov     a,R3            ; get low byte
1081         movx    @DPTR,A         ; store in FIFO
1082         inc     dptr            ; next fifo entry
1083         mov     a,R4            ; get high byte
1084         movx    @DPTR,A         ; store in FIFO
1085
1086         sjmp    ep8_send        ; send the data
1087
1088         ;; read the digital lines
1089 ep8_dio:        
1090         mov     DPTR,#0fc02h    ; store the contents of port B
1091         mov     a,IOB           ; in the next
1092         movx    @dptr,a         ; entry of the buffer
1093
1094         inc     dptr
1095         clr     a               ; high byte is zero
1096         movx    @dptr,a         ; next byte of the EP
1097         
1098 ep8_send:       
1099         mov     DPTR,#EP8BCH    ; byte count H
1100         mov     a,#0            ; is zero
1101         lcall   syncdelaywr
1102         
1103         mov     DPTR,#EP8BCL    ; byte count L
1104         mov     a,#10H          ; 16 bytes
1105         lcall   syncdelaywr     ; send the data over to the host
1106
1107 ep8_err:        
1108         ret
1109
1110
1111
1112 ;;; EP8 interrupt: gets one measurement from the AD converter and
1113 ;;; sends it via EP8. The channel # is stored in address 80H.
1114 ;;; It also gets the state of the digital registers B and D.
1115 ep8_isr:        
1116         push    dps
1117         push    dpl
1118         push    dph
1119         push    dpl1
1120         push    dph1
1121         push    acc
1122         push    psw
1123         push    00h             ; R0
1124         push    01h             ; R1
1125         push    02h             ; R2
1126         push    03h             ; R3
1127         push    04h             ; R4
1128         push    05h             ; R5
1129         push    06h             ; R6
1130         push    07h             ; R7
1131                 
1132         lcall   ep8_ops
1133         
1134         ;; clear INT2
1135         mov     a,EXIF          ; FIRST clear the USB (INT2) interrupt request
1136         clr     acc.4
1137         mov     EXIF,a          ; Note: EXIF reg is not 8051 bit-addressable
1138
1139         mov     DPTR,#EPIRQ     ; 
1140         mov     a,#10000000b    ; clear the ep8irq
1141         movx    @DPTR,a
1142
1143         pop     07h
1144         pop     06h
1145         pop     05h
1146         pop     04h             ; R4
1147         pop     03h             ; R3
1148         pop     02h             ; R2
1149         pop     01h             ; R1
1150         pop     00h             ; R0
1151         pop     psw
1152         pop     acc 
1153         pop     dph1 
1154         pop     dpl1
1155         pop     dph 
1156         pop     dpl 
1157         pop     dps
1158         reti
1159
1160
1161 ;; need to delay every time the byte counters
1162 ;; for the EPs have been changed.
1163
1164 syncdelay:
1165         nop
1166         nop
1167         nop
1168         nop
1169         nop
1170         nop
1171         nop
1172         nop
1173         nop
1174         ret
1175
1176 syncdelaywr:
1177         movx    @dptr,a
1178         lcall   syncdelay
1179         ret
1180
1181
1182 .End
1183
1184