carl9170: Update to latest upstream
[linux-libre-firmware.git] / usbdux / usbduxsigma_firmware.asm
1 ;   usbdux_firmware.asm
2 ;   Copyright (C) 2010,2015 Bernd Porr, mail@berndporr.me.uk
3 ;   For usbduxsigma.c 0.5+
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: usbduxsigma_firmware.asm for usbduxsigma.c
21 ; Description: University of Stirling USB DAQ & INCITE Technology Limited
22 ; Devices: [ITL] USB-DUX-SIGMA (usbduxsigma.ko)
23 ; Author: Bernd Porr <mail@berndporr.me.uk>
24 ; Updated: 20 Jul 2015
25 ; Status: testing
26 ;
27 ;;;
28 ;;;
29 ;;;
30         
31         .inc    fx2-include.asm
32
33 ;;; a couple of flags in high memory
34         .equ    CMD_FLAG,80h    ; flag for the next in transfer
35         .equ    PWMFLAG,81h     ; PWM on or off?
36         .equ    MAXSMPL,82H     ; maximum number of samples, n channellist
37         .equ    MUXSG0,83H      ; content of the MUXSG0 register
38         .equ    INTERVAL,88h    ; uframe/frame interval
39         .equ    INTCTR,89h      ; interval counter
40         .equ    DABUFFER,0F0h   ; buffer with DA values
41
42 ;;; in precious low memory but accessible within one clock cycle
43         .equ    DPTRL,70H
44         .equ    DPTRH,71h
45         .equ    ASYNC_ON,72h
46         .equ    SMPLCTR,73h
47
48 ;;; actual code
49         .org    0000h           ; after reset the processor starts here
50         ljmp    main            ; jump to the main loop
51
52         .org    0003h
53         ljmp    isr0            ; external interrupt 0: /DRY
54
55         .org    0043h           ; the IRQ2-vector
56         ljmp    jmptbl          ; irq service-routine
57
58         .org    0100h           ; start of the jump table
59
60 jmptbl: ljmp    sudav_isr
61         nop
62         ljmp    sof_isr
63         nop
64         ljmp    sutok_isr
65         nop
66         ljmp    suspend_isr
67         nop
68         ljmp    usbreset_isr
69         nop
70         ljmp    hispeed_isr
71         nop
72         ljmp    ep0ack_isr
73         nop
74         ljmp    spare_isr
75         nop
76         ljmp    ep0in_isr
77         nop
78         ljmp    ep0out_isr
79         nop
80         ljmp    ep1in_isr
81         nop
82         ljmp    ep1out_isr
83         nop
84         ljmp    ep2_isr
85         nop
86         ljmp    ep4_isr
87         nop
88         ljmp    ep6_isr
89         nop
90         ljmp    ep8_isr
91         nop
92         ljmp    ibn_isr
93         nop
94         ljmp    spare_isr
95         nop
96         ljmp    ep0ping_isr
97         nop
98         ljmp    ep1ping_isr
99         nop
100         ljmp    ep2ping_isr
101         nop
102         ljmp    ep4ping_isr
103         nop
104         ljmp    ep6ping_isr
105         nop
106         ljmp    ep8ping_isr
107         nop
108         ljmp    errlimit_isr
109         nop
110         ljmp    spare_isr
111         nop
112         ljmp    spare_isr
113         nop
114         ljmp    spare_isr
115         nop
116         ljmp    ep2isoerr_isr
117         nop
118         ljmp    ep4isoerr_isr
119         nop
120         ljmp    ep6isoerr_isr
121         nop
122         ljmp    ep8isoerr_isr
123
124         
125         ;; dummy isr
126 sudav_isr:      
127 sutok_isr:      
128 suspend_isr:    
129 usbreset_isr:   
130 hispeed_isr:    
131 ep0ack_isr:     
132 spare_isr:      
133 ep0in_isr:      
134 ep0out_isr:     
135 ibn_isr:        
136 ep0ping_isr:    
137 ep1ping_isr:    
138 ep2ping_isr:    
139 ep4ping_isr:    
140 ep6ping_isr:    
141 ep8ping_isr:    
142 errlimit_isr:   
143 ep2isoerr_isr:  
144 ep4isoerr_isr:  
145 ep6isoerr_isr:  
146 ep8isoerr_isr:
147 ep6_isr:
148 ep2_isr:
149 ep4_isr:        
150
151         push    dps
152         push    dpl
153         push    dph
154         push    dpl1
155         push    dph1
156         push    acc
157         push    psw
158
159         ;; clear the USB2 irq bit and return
160         mov     a,EXIF
161         clr     acc.4
162         mov     EXIF,a
163
164         pop     psw
165         pop     acc 
166         pop     dph1 
167         pop     dpl1
168         pop     dph 
169         pop     dpl 
170         pop     dps
171         
172         reti
173
174
175 ep1in_isr:      
176         push    dps
177         push    dpl
178         push    dph
179         push    dpl1
180         push    dph1
181         push    acc
182         push    psw
183                 
184         mov     dptr,#0E7C0h    ; EP1in
185         mov     a,IOB           ; get DIO D
186         movx    @dptr,a         ; store it
187         inc     dptr            ; next byte
188         mov     a,IOC           ; get DIO C
189         movx    @dptr,a         ; store it
190         inc     dptr            ; next byte
191         mov     a,IOD           ; get DIO B
192         movx    @dptr,a         ; store it
193         inc     dptr            ; next byte
194         mov     a,#0            ; just zero
195         movx    @dptr,a         ; pad it up
196
197         ;; clear INT2
198         mov     a,EXIF          ; FIRST clear the USB (INT2) interrupt request
199         clr     acc.4
200         mov     EXIF,a          ; Note: EXIF reg is not 8051 bit-addressable
201
202         mov     DPTR,#EPIRQ     ; 
203         mov     a,#00000100b    ; clear the ep1in
204         movx    @DPTR,a
205
206         pop     psw
207         pop     acc 
208         pop     dph1 
209         pop     dpl1
210         pop     dph 
211         pop     dpl 
212         pop     dps
213         reti
214
215
216
217 ;;; this is triggered when DRY goes low
218 isr0:   
219         push    dps
220         push    dpl
221         push    dph
222         push    dpl1
223         push    dph1
224         push    acc
225         push    psw
226         push    00h             ; R0
227         push    01h             ; R1
228         push    02h             ; R2
229         push    03h             ; R3
230         push    04h             ; R4
231         push    05h             ; R5
232         push    06h             ; R6
233         push    07h             ; R7
234
235         mov     a,ASYNC_ON
236         jz      noepsubmit
237
238         mov     DPS,#0
239         mov     dpl,DPTRL
240         mov     dph,DPTRH
241
242         lcall   readADCch       ; read one channel
243
244         mov     DPTRL,dpl
245         mov     DPTRH,dph
246
247         mov     a,SMPLCTR
248         dec     a
249         mov     SMPLCTR,a
250         jnz     noepsubmit
251
252         mov     ASYNC_ON,#0
253
254         clr     IOA.7           ; START = 0
255         
256         ;; arm the endpoint and send off the data
257         mov     DPTR,#EP6BCH    ; byte count H
258         mov     a,#0            ; is zero
259         lcall   syncdelaywr     ; wait until we can write again
260         
261         mov     r0,#MAXSMPL     ; number of samples to transmit
262         mov     a,@r0           ; get them
263         rl      a               ; a=a*2
264         rl      a               ; a=a*2
265         add     a,#4            ; four bytes for DIO
266         mov     DPTR,#EP6BCL    ; byte count L
267         lcall   syncdelaywr     ; wait until we can write again
268
269 noepsubmit:
270         pop     07h
271         pop     06h
272         pop     05h
273         pop     04h             ; R4
274         pop     03h             ; R3
275         pop     02h             ; R2
276         pop     01h             ; R1
277         pop     00h             ; R0
278         pop     psw
279         pop     acc 
280         pop     dph1 
281         pop     dpl1
282         pop     dph 
283         pop     dpl 
284         pop     dps
285
286         reti
287
288         
289                 
290 ;;; main program
291 ;;; basically only initialises the processor and
292 ;;; then engages in an endless loop
293 main:
294         mov     DPTR,#CPUCS     ; CPU control register
295         mov     a,#00010000b    ; 48Mhz
296         lcall   syncdelaywr
297
298         mov     dptr,#REVCTL
299         mov     a,#00000011b    ; allows skip
300         lcall   syncdelaywr
301
302         mov     dptr,#INTSETUP  ; IRQ setup register
303         mov     a,#08h          ; enable autovector
304         lcall   syncdelaywr
305
306         mov     dptr,#PORTCCFG
307         mov     a,#0
308         lcall   syncdelaywr
309
310         mov     IP,#01H         ; int0 has highest interrupt priority
311         mov     EIP,#0          ; all USB interrupts have low priority
312
313         lcall   initAD          ; init the ports to the converters
314
315         lcall   initeps         ; init the isochronous data-transfer
316
317 ;;; main loop, rest is done as interrupts
318 mloop2: nop
319
320 ;;; pwm
321         mov     r0,#PWMFLAG     ; pwm on?
322         mov     a,@r0           ; get info
323         jz      mloop2          ; it's off
324
325         mov     a,GPIFTRIG      ; GPIF status
326         anl     a,#80h          ; done bit
327         jz      mloop2          ; GPIF still busy
328
329         mov     a,#01h          ; WR,EP4, 01 = EP4
330         mov     GPIFTRIG,a      ; restart it
331
332         sjmp    mloop2          ; loop for ever
333
334
335 ;;; initialise the ports for the AD-converter
336 initAD:
337         mov     r0,#MAXSMPL     ; length of channellist
338         mov     @r0,#0          ; we don't want to accumlate samples
339
340         mov     ASYNC_ON,#0     ; async enable
341
342         mov     r0,#DABUFFER
343         mov     @r0,#0
344
345         mov     OEA,#11100000b  ; PortA7,A6,A5 Outputs
346         mov     IOA,#01100000b  ; /CS = 1 and START = 0
347         mov     dptr,#IFCONFIG  ; switch on clock on IFCLK pin
348         mov     a,#10100000b    ; gpif, 30MHz, internal IFCLK -> 15MHz for AD
349         lcall   syncdelaywr
350
351         mov     SCON0,#013H     ; ser rec en, TX/RX: stop, 48/12MHz=4MHz clock
352         
353         mov     dptr,#PORTECFG
354         mov     a,#00001000b    ; special function for port E: RXD0OUT
355         lcall   syncdelaywr
356
357         ret
358
359
360 ;;; send a byte via SPI
361 ;;; content in a, dptr1 is changed
362 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
363 ;;; important: /cs needs to be reset to 1 by the caller: IOA.5
364 sendSPI:
365         inc     DPS
366         
367         ;; bit reverse
368         mov     dptr,#swap_lut  ; lookup table
369         movc    a,@a+dptr       ; reverse bits
370
371         ;; clear interrupt flag, is used to detect
372         ;; successful transmission
373         clr     SCON0.1         ; clear interrupt flag
374
375         ;; start transmission by writing the byte
376         ;; in the transmit buffer
377         mov     SBUF0,a         ; start transmission
378
379         ;; wait for the end of the transmission
380 sendSPIwait:
381         mov     a,SCON0         ; get transmission status
382         jnb     ACC.1,sendSPIwait       ; loop until transmitted
383
384         inc     DPS
385         
386         ret
387
388
389
390         
391 ;;; receive a byte via SPI
392 ;;; content in a, dptr is changed
393 ;;; the lookup is done in dptr1 so that the normal dptr is not affected
394 ;;; important: the /CS needs to be set to 1 by the caller via "setb IOA.5"
395 recSPI:
396         inc     DPS
397         
398         clr     IOA.5           ; /cs to 0      
399
400         ;; clearning the RI bit starts reception of data
401         clr     SCON0.0
402
403 recSPIwait:
404         ;; RI goes back to 1 after the reception of the 8 bits
405         mov     a,SCON0         ; get receive status
406         jnb     ACC.0,recSPIwait; loop until all bits received
407
408         ;; read the byte from the buffer
409         mov     a,SBUF0         ; get byte
410         
411         ;; lookup: reverse the bits
412         mov     dptr,#swap_lut  ; lookup table
413         movc    a,@a+dptr       ; reverse the bits
414
415         inc     DPS
416         
417         ret
418
419
420
421         
422 ;;; reads a register
423 ;;; register address in a
424 ;;; returns value in a
425 registerRead:
426         anl     a,#00001111b    ; mask out the index to the register
427         orl     a,#01000000b    ; 010xxxxx indicates register read
428         clr     IOA.5           ; ADC /cs to 0
429         lcall   sendSPI         ; send the command over
430         lcall   recSPI          ; read the contents back
431         setb    IOA.5           ; ADC /cs to 1
432         ret
433
434
435
436 ;;; writes to a register
437 ;;; register address in a
438 ;;; value in r0
439 registerWrite:
440         push    acc
441         anl     a,#00001111b    ; mask out the index to the register
442         orl     a,#01100000b    ; 011xxxxx indicates register write
443
444         clr     IOA.5           ; ADC /cs to 0  
445
446         lcall   sendSPI         ;
447         mov     a,r0
448         lcall   sendSPI
449
450         setb    IOA.5           ; ADC /cs to 1
451         pop     acc
452
453         lcall   registerRead    ; check if the data has arrived in the ADC
454         mov     0f0h,r0         ; register B
455         cjne    a,0f0h,registerWrite ; something went wrong, try again
456         
457         ret
458
459
460
461 ;;; initilise the endpoints
462 initeps:
463         mov     dptr,#FIFORESET
464         mov     a,#80H          
465         movx    @dptr,a         ; reset all fifos
466         mov     a,#2    
467         movx    @dptr,a         ; 
468         mov     a,#4            
469         movx    @dptr,a         ; 
470         mov     a,#6            
471         movx    @dptr,a         ; 
472         mov     a,#8            
473         movx    @dptr,a         ; 
474         mov     a,#0            
475         movx    @dptr,a         ; normal operat
476         
477         mov     DPTR,#EP2CFG
478         mov     a,#10010010b    ; valid, out, double buff, iso
479         movx    @DPTR,a
480
481         mov     dptr,#EP2FIFOCFG
482         mov     a,#00000000b    ; manual
483         movx    @dptr,a
484
485         mov     dptr,#EP2BCL    ; "arm" it
486         mov     a,#00h
487         movx    @DPTR,a         ; can receive data
488         lcall   syncdelay       ; wait to sync
489         movx    @DPTR,a         ; can receive data
490         lcall   syncdelay       ; wait to sync
491         movx    @DPTR,a         ; can receive data
492         lcall   syncdelay       ; wait to sync
493         
494         mov     DPTR,#EP1OUTCFG
495         mov     a,#10100000b    ; valid
496         movx    @dptr,a
497
498         mov     dptr,#EP1OUTBC  ; "arm" it
499         mov     a,#00h
500         movx    @DPTR,a         ; can receive data
501         lcall   syncdelay       ; wait until we can write again
502         movx    @dptr,a         ; make shure its really empty
503         lcall   syncdelay       ; wait
504
505         mov     DPTR,#EP6CFG    ; ISO data from here to the host
506         mov     a,#11010010b    ; Valid
507         movx    @DPTR,a         ; ISO transfer, double buffering
508
509         mov     DPTR,#EP8CFG    ; EP8
510         mov     a,#11100000b    ; BULK data from here to the host
511         movx    @DPTR,a         ;
512
513         mov     dptr,#PORTACFG
514         mov     a,#1            ; interrupt on pin A0
515         lcall   syncdelaywr
516
517         ;; enable interrupts
518         mov     dptr,#EPIE      ; interrupt enable
519         mov     a,#10001100b    ; enable irq for ep1out,8,ep1in
520         movx    @dptr,a         ; do it
521
522         mov     dptr,#EPIRQ     ; clear IRQs
523         mov     a,#10001100b
524         movx    @dptr,a
525         
526         mov     DPTR,#USBIE     ; USB int enables register
527         mov     a,#2            ; enables SOF (1ms/125us interrupt)
528         movx    @DPTR,a         ; 
529
530         setb    TCON.0          ; make INT0 edge triggered, falling edge
531
532         mov     EIE,#00000001b  ; enable INT2/USBINT in the 8051's SFR
533         mov     IE,#81h         ; IE, enable all interrupts and INT0
534
535         ret
536
537
538 ;;; Reads one ADC channel from the converter and stores
539 ;;; the result at dptr
540 readADCch:
541         ;; reading data is done by just dropping /CS and start reading and
542         ;; while keeping the IN signal to the ADC inactive
543         clr     IOA.5           ; /cs to 0
544         
545         ;; 1st byte: STATUS
546         lcall   recSPI          ; index
547         movx    @dptr,a         ; store the byte
548         inc     dptr            ; increment pointer
549
550         ;; 2nd byte: MSB
551         lcall   recSPI          ; data
552         movx    @dptr,a
553         inc     dptr
554
555         ;; 3rd byte: MSB-1
556         lcall   recSPI          ; data
557         movx    @dptr,a
558         inc     dptr
559
560         ;; 4th byte: LSB
561         lcall   recSPI          ; data
562         movx    @dptr,a
563         inc     dptr
564         
565         ;; got all bytes
566         setb    IOA.5           ; /cs to 1
567         
568         ret
569
570         
571
572 ;;; interrupt-routine for SOF
573 sof_isr:
574         push    dps
575         push    dpl
576         push    dph
577         push    dpl1
578         push    dph1
579         push    acc
580         push    psw
581         push    00h             ; R0
582         push    01h             ; R1
583         push    02h             ; R2
584         push    03h             ; R3
585         push    04h             ; R4
586         push    05h             ; R5
587         push    06h             ; R6
588         push    07h             ; R7
589
590         mov     r0,#INTCTR      ; interval counter
591         mov     a,@r0           ; get the value
592         dec     a               ; decrement
593         mov     @r0,a           ; save it again
594         jz      sof_adc         ; we do ADC functions
595         ljmp    epfull          ; we skip all adc functions
596         
597 sof_adc:
598         mov     r1,#INTERVAL    ; get the interval
599         mov     a,@r1           ; get it
600         mov     @r0,a           ; save it in the counter
601         mov     a,EP2468STAT
602         anl     a,#20H          ; full?
603         jnz     epfull          ; EP6-buffer is full
604
605         mov     a,IOA           ; conversion running?
606         jb      ACC.7,epfull
607
608         ;; make sure that we are starting with the first channel
609         mov     r0,#MUXSG0      ;
610         mov     a,@r0           ; get config of MUXSG0
611         mov     r0,a
612         mov     a,#04H          ; MUXSG0
613         lcall   registerWrite   ; this resets the channel sequence
614
615         setb    IOA.7           ; start converter, START = 1
616         
617         mov     dptr,#0f800h    ; EP6 buffer
618         mov     a,IOD           ; get DIO D
619         movx    @dptr,a         ; store it
620         inc     dptr            ; next byte
621         mov     a,IOC           ; get DIO C
622         movx    @dptr,a         ; store it
623         inc     dptr            ; next byte
624         mov     a,IOB           ; get DIO B
625         movx    @dptr,a         ; store it
626         inc     dptr            ; next byte
627         mov     a,#0            ; just zero
628         movx    @dptr,a         ; pad it up
629         inc     dptr            ; algin along a 32 bit word
630         mov     DPTRL,dpl
631         mov     DPTRH,dph
632
633         mov     r0,#MAXSMPL
634         mov     a,@r0
635         mov     SMPLCTR,a
636
637         mov     ASYNC_ON,#1
638
639 epfull:
640         ;; do the D/A conversion
641         mov     a,EP2468STAT
642         anl     a,#01H          ; empty
643         jnz     epempty         ; nothing to get
644
645         mov     dptr,#0F000H    ; EP2 fifo buffer
646         lcall   dalo            ; conversion
647
648         mov     dptr,#EP2BCL    ; "arm" it
649         mov     a,#00h
650         lcall   syncdelaywr     ; wait for the rec to sync
651         lcall   syncdelaywr     ; wait for the rec to sync
652
653 epempty:
654         mov     a,IOA           ; conversion running?
655         jb      ACC.7,sofend
656
657         lcall   DAsend
658
659 sofend: 
660         ;; clear INT2
661         mov     a,EXIF          ; FIRST clear the USB (INT2) interrupt request
662         clr     acc.4
663         mov     EXIF,a          ; Note: EXIF reg is not 8051 bit-addressable
664         
665         mov     DPTR,#USBIRQ    ; points to the SOF
666         mov     a,#2            ; clear the SOF
667         movx    @DPTR,a
668
669 nosof:
670         pop     07h
671         pop     06h
672         pop     05h
673         pop     04h             ; R4
674         pop     03h             ; R3
675         pop     02h             ; R2
676         pop     01h             ; R1
677         pop     00h             ; R0
678         pop     psw
679         pop     acc 
680         pop     dph1 
681         pop     dpl1
682         pop     dph 
683         pop     dpl 
684         pop     dps
685         reti
686
687
688 reset_ep8:
689         ;; erase all data in ep8
690         mov     dptr,#FIFORESET
691         mov     a,#80H          ; NAK
692         lcall   syncdelaywr
693         mov     dptr,#FIFORESET
694         mov     a,#8            ; reset EP8
695         lcall   syncdelaywr
696         mov     dptr,#FIFORESET
697         mov     a,#0            ; normal operation
698         lcall   syncdelaywr
699         ret
700
701
702 reset_ep6:
703         ;; throw out old data
704         mov     dptr,#FIFORESET
705         mov     a,#80H          ; NAK
706         lcall   syncdelaywr
707         mov     dptr,#FIFORESET
708         mov     a,#6            ; reset EP6
709         lcall   syncdelaywr
710         mov     dptr,#FIFORESET
711         mov     a,#0            ; normal operation
712         lcall   syncdelaywr
713         ret
714
715
716 ;;; configure the ADC converter
717 ;;; the dptr points to the init data:
718 ;;; CONFIG 0,1,3,4,5,6
719 ;;; note that CONFIG2 is omitted
720 configADC:      
721         clr     IOA.7           ; stops ADC: START line of ADC = L
722         setb    IOA.5           ; ADC /cs to 1
723
724         ;; just in case something has gone wrong
725         nop
726         nop
727         nop
728
729         mov     a,#11000000b    ; reset the ADC
730         clr     IOA.5           ; ADC /cs to 0  
731         lcall   sendSPI
732         setb    IOA.5           ; ADC /cs to 1  
733
734         movx    a,@dptr         ;
735         inc     dptr
736         mov     r0,a
737         mov     a,#00H          ; CONFIG0
738         lcall   registerWrite
739
740         movx    a,@dptr         ;
741         inc     dptr
742         mov     r0,a
743         mov     a,#01H          ; CONFIG1
744         lcall   registerWrite
745
746         movx    a,@dptr         ;
747         inc     dptr
748         mov     r0,a
749         mov     a,#03H          ; MUXDIF
750         lcall   registerWrite
751
752         movx    a,@dptr         ;
753         inc     dptr
754         mov     r0,#MUXSG0
755         mov     @r0,a           ; store it for reset purposes
756         mov     r0,a
757         mov     a,#04H          ; MUXSG0
758         lcall   registerWrite
759         
760         movx    a,@dptr         ;
761         inc     dptr
762         mov     r0,a
763         mov     a,#05H          ; MUXSG1
764         lcall   registerWrite
765         
766         movx    a,@dptr         ;
767         inc     dptr
768         mov     r0,a
769         mov     a,#06H          ; SYSRED
770         lcall   registerWrite
771
772         ret
773
774         
775 ;;; interrupt-routine for ep1out
776 ;;; receives the channel list and other commands
777 ep1out_isr:
778         push    dps
779         push    dpl
780         push    dph
781         push    dpl1
782         push    dph1
783         push    acc
784         push    psw
785         push    00h             ; R0
786         push    01h             ; R1
787         push    02h             ; R2
788         push    03h             ; R3
789         push    04h             ; R4
790         push    05h             ; R5
791         push    06h             ; R6
792         push    07h             ; R7
793
794         mov     dptr,#0E780h    ; FIFO buffer of EP1OUT
795         movx    a,@dptr         ; get the first byte
796         mov     r0,#CMD_FLAG    ; pointer to the command byte
797         mov     @r0,a           ; store the command byte for ep8
798
799         mov     dptr,#ep1out_jmp; jump table for the different functions
800         rl      a               ; multiply by 2: sizeof sjmp
801         jmp     @a+dptr         ; jump to the jump table
802         ;; jump table, corresponds to the command bytes defined
803         ;; in usbdux.c
804 ep1out_jmp:
805         sjmp    startadc        ; a=0
806         sjmp    single_da       ; a=1
807         sjmp    config_digital_b; a=2
808         sjmp    write_digital_b ; a=3
809         sjmp    initsgADchannel ; a=4
810         sjmp    nothing         ; a=5
811         sjmp    nothing         ; a=6
812         sjmp    pwm_on          ; a=7
813         sjmp    pwm_off         ; a=8
814         sjmp    startadcint     ; a=9
815
816 nothing:
817         ljmp    over_da
818
819 pwm_on:
820         lcall   startPWM
821         sjmp    over_da
822
823 pwm_off:
824         lcall   stopPWM
825         sjmp    over_da
826
827 initsgADchannel:
828         mov     ASYNC_ON,#0
829         
830         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
831         lcall   configADC       ; configures the ADC esp sel the channel
832
833         lcall   reset_ep8       ; reset FIFO: get rid of old bytes
834         ;; Save new A/D data in EP8. This is the first byte
835         ;; the host will read during an INSN. If there are
836         ;; more to come they will be handled by the ISR of
837         ;; ep8.
838         lcall   ep8_ops         ; get A/D data
839                 
840         sjmp    over_da
841
842 startadcint:
843         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT from 2nd byte
844
845         movx    a,@dptr         ; interval is the 1st byte
846         inc     dptr            ; data pointer
847         sjmp    startadc2       ; the other paramters as with startadc
848         
849 ;;; config AD:
850 ;;; we write to the registers of the A/D converter
851 startadc:
852         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT from 2nd byte
853
854         mov     a,#1            ; interval is 1 here all the time
855 startadc2:      
856         mov     r0,#INTERVAL    ; set it
857         mov     @r0,a
858         mov     r0,#INTCTR      ; the counter is also just one
859         mov     @r0,a
860
861         movx    a,@dptr         ; get length of channel list
862         inc     dptr
863         mov     r0,#MAXSMPL
864         mov     @r0,a           ; length of the channel list
865         mov     SMPLCTR,a
866
867         lcall   configADC       ; configures all registers
868
869         mov     ASYNC_ON,#1     ; async enable
870
871         lcall   reset_ep6       ; reset FIFO
872         
873         ;; load new A/D data into EP6
874         ;; This must be done. Otherwise the ISR is never called.
875         ;; The ISR is only called when data has _left_ the
876         ;; ep buffer here it has to be refilled.
877         lcall   ep6_arm         ; fill with dummy data
878         
879         sjmp    over_da
880
881 ;;; Single DA conversion. The 2 bytes are in the FIFO buffer
882 single_da:
883         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
884         lcall   dalo            ; conversion
885         sjmp    over_da
886
887 ;;; configure the port B as input or output (bitwise)
888 config_digital_b:
889         mov     dptr,#0e781h    ; FIFO buffer of EP1OUT
890         movx    a,@dptr         ; get the second byte
891         inc     dptr
892         mov     OEB,a           ; set the output enable bits
893         movx    a,@dptr         ; get the second byte
894         inc     dptr
895         mov     OEC,a
896         movx    a,@dptr         ; get the second byte
897         inc     dptr
898         mov     OED,a
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         ; command[1]
906         inc     dptr
907         mov     OEB,a           ; output enable
908         movx    a,@dptr         ; command[2]
909         inc     dptr
910         mov     OEC,a
911         movx    a,@dptr         ; command[3]
912         inc     dptr
913         mov     OED,a 
914         movx    a,@dptr         ; command[4]
915         inc     dptr
916         mov     IOB,a           ;
917         movx    a,@dptr         ; command[5]
918         inc     dptr
919         mov     IOC,a
920         movx    a,@dptr         ; command[6]
921         inc     dptr
922         mov     IOD,a
923
924         lcall   reset_ep8       ; reset FIFO of ep 8
925
926         ;; fill ep8 with new data from port B
927         ;; When the host requests the data it's already there.
928         ;; This must be so. Otherwise the ISR is not called.
929         ;; The ISR is only called when a packet has been delivered
930         ;; to the host. Thus, we need a packet here in the
931         ;; first instance.
932         lcall   ep8_ops         ; get digital data
933
934         ;; 
935         ;; for all commands the same
936 over_da:        
937         mov     dptr,#EP1OUTBC
938         mov     a,#00h
939         lcall   syncdelaywr     ; arm
940         lcall   syncdelaywr     ; arm
941         lcall   syncdelaywr     ; arm
942
943         ;; clear INT2
944         mov     a,EXIF          ; FIRST clear the USB (INT2) interrupt request
945         clr     acc.4
946         mov     EXIF,a          ; Note: EXIF reg is not 8051 bit-addressable
947
948         mov     DPTR,#EPIRQ     ; 
949         mov     a,#00001000b    ; clear the ep1outirq
950         movx    @DPTR,a
951
952         pop     07h
953         pop     06h
954         pop     05h
955         pop     04h             ; R4
956         pop     03h             ; R3
957         pop     02h             ; R2
958         pop     01h             ; R1
959         pop     00h             ; R0
960         pop     psw
961         pop     acc 
962         pop     dph1 
963         pop     dpl1
964         pop     dph 
965         pop     dpl 
966         pop     dps
967         reti
968
969
970         
971 ;;; save all DA channels from the endpoint buffer in a local buffer
972 dalo:
973         movx    a,@dptr         ; number of bytes to send out
974         inc     dptr            ; pointer to the first byte
975         mov     r1,#DABUFFER    ; buffer for DA values
976         mov     @r1,a           ; save it
977         inc     r1              ; inc pointer to local buffer
978         mov     r0,a            ; counter
979 nextDAlo:       
980         movx    a,@dptr         ; get the byte
981         inc     dptr            ; point to the high byte
982         mov     @r1,a           ; save it in the buffer
983         inc     r1
984         movx    a,@dptr         ; get the channel number
985         inc     dptr            ; get ready for the next channel
986         mov     @r1,a           ; save it
987         inc     r1
988         djnz    r0,nextDAlo     ; next channel
989         ret
990
991
992 ;;; write to the DA converter
993 DAsend:
994         mov     r1,#DABUFFER    ; buffer of the DA values
995         mov     a,@r1           ; get the channel count
996         jz      DAret           ; nothing to do
997         inc     r1              ; pointer to the first byte
998         mov     r0,a            ; counter
999 nextDA: 
1000         mov     a,@r1           ; get the byte
1001         inc     r1              ; point to the high byte
1002         mov     r3,a            ; store in r3 for writeDA
1003         mov     a,@r1           ; get the channel number
1004         inc     r1              ; get ready for the next channel
1005         push    1               ; is modified in the subroutine
1006         lcall   writeDA         ; write value to the DAC
1007         pop     1               ; get the pointer back
1008         djnz    r0,nextDA       ; next channel
1009 DAret:  
1010         ret
1011
1012
1013
1014 ;;; D/A-conversion:
1015 ;;; channel number in a
1016 ;;; value in r3
1017 writeDA:
1018         anl     a,#00000011b    ; 4 channels
1019         mov     r1,#6           ; the channel number needs to be shifted up
1020 writeDA2:
1021         rl      a               ; bit shift to the left
1022         djnz    r1,writeDA2     ; do it 6 times
1023         orl     a,#00010000b    ; update outputs after write
1024         mov     r2,a            ; backup
1025         mov     a,r3            ; get byte
1026         anl     a,#11110000b    ; get the upper nibble
1027         mov     r1,#4           ; shift it up to the upper nibble
1028 writeDA3:
1029         rr      a               ; shift to the upper to the lower
1030         djnz    r1,writeDA3
1031         orl     a,r2            ; merge with the channel info
1032         clr     IOA.6           ; /SYNC (/CS) of the DA to 0
1033         lcall   sendSPI         ; send it out to the SPI
1034         mov     a,r3            ; get data again
1035         anl     a,#00001111b    ; get the lower nibble
1036         mov     r1,#4           ; shift that to the upper
1037 writeDA4:
1038         rl      a
1039         djnz    r1,writeDA4
1040         anl     a,#11110000b    ; make sure that's empty
1041         lcall   sendSPI
1042         setb    IOA.6           ; /SYNC (/CS) of the DA to 1
1043 noDA:   ret
1044         
1045
1046
1047 ;;; arm ep6: this is just a dummy arm to get things going
1048 ep6_arm:
1049         mov     DPTR,#EP6BCH    ; byte count H
1050         mov     a,#0            ; is zero
1051         lcall   syncdelaywr     ; wait until the length has arrived
1052         
1053         mov     DPTR,#EP6BCL    ; byte count L
1054         mov     a,#1            ; is one
1055         lcall   syncdelaywr     ; wait until the length has been proc
1056         ret
1057         
1058
1059
1060 ;;; converts one analog/digital channel and stores it in EP8
1061 ;;; also gets the content of the digital ports B,C and D depending on
1062 ;;; the COMMAND flag
1063 ep8_ops:
1064         mov     dptr,#0fc01h    ; ep8 fifo buffer
1065         clr     a               ; high byte
1066         movx    @dptr,a         ; set H=0
1067         mov     dptr,#0fc00h    ; low byte
1068         mov     r0,#CMD_FLAG
1069         mov     a,@r0
1070         movx    @dptr,a         ; save command byte
1071
1072         mov     dptr,#ep8_jmp   ; jump table for the different functions
1073         rl      a               ; multiply by 2: sizeof sjmp
1074         jmp     @a+dptr         ; jump to the jump table
1075         ;; jump table, corresponds to the command bytes defined
1076         ;; in usbdux.c
1077 ep8_jmp:
1078         sjmp    ep8_err         ; a=0, err
1079         sjmp    ep8_err         ; a=1, err
1080         sjmp    ep8_err         ; a=2, err
1081         sjmp    ep8_dio         ; a=3, digital read
1082         sjmp    ep8_sglchannel  ; a=4, analog A/D
1083         sjmp    ep8_err         ; a=5, err
1084         sjmp    ep8_err         ; a=6, err
1085
1086         ;; read one A/D channel
1087 ep8_sglchannel:
1088         setb    IOA.7           ; start converter, START = 1
1089         ;; we do polling: we wait until DATA READY is zero
1090 sglchwait:      
1091         mov     a,IOA           ; get /DRDY
1092         jb      ACC.0,sglchwait ; wait until data ready (DRDY=0)
1093         mov     DPTR,#0fc01h    ; EP8 FIFO
1094         lcall   readADCch       ; get one reading
1095         clr     IOA.7           ; stop the converter, START = 0
1096
1097         sjmp    ep8_send        ; send the data
1098
1099         ;; read the digital lines
1100 ep8_dio:        
1101         mov     DPTR,#0fc01h    ; store the contents of port B
1102         mov     a,IOB           ; in the next
1103         movx    @dptr,a         ; entry of the buffer
1104         inc     dptr
1105         mov     a,IOC           ; port C
1106         movx    @dptr,a         ; next byte of the EP
1107         inc     dptr
1108         mov     a,IOD
1109         movx    @dptr,a         ; port D
1110         
1111 ep8_send:       
1112         mov     DPTR,#EP8BCH    ; byte count H
1113         mov     a,#0            ; is zero
1114         lcall   syncdelaywr
1115         
1116         mov     DPTR,#EP8BCL    ; byte count L
1117         mov     a,#10H          ; 16 bytes, bec it's such a great number...
1118         lcall   syncdelaywr     ; send the data over to the host
1119
1120 ep8_err:        
1121         ret
1122
1123
1124
1125 ;;; EP8 interrupt is the endpoint which sends data back after a command
1126 ;;; The actual command fills the EP buffer already
1127 ;;; but for INSNs we need to deliver more data if the count > 1
1128 ep8_isr:        
1129         push    dps
1130         push    dpl
1131         push    dph
1132         push    dpl1
1133         push    dph1
1134         push    acc
1135         push    psw
1136         push    00h             ; R0
1137         push    01h             ; R1
1138         push    02h             ; R2
1139         push    03h             ; R3
1140         push    04h             ; R4
1141         push    05h             ; R5
1142         push    06h             ; R6
1143         push    07h             ; R7
1144                 
1145         lcall   ep8_ops
1146         
1147         ;; clear INT2
1148         mov     a,EXIF          ; FIRST clear the USB (INT2) interrupt request
1149         clr     acc.4
1150         mov     EXIF,a          ; Note: EXIF reg is not 8051 bit-addressable
1151
1152         mov     DPTR,#EPIRQ     ; 
1153         mov     a,#10000000b    ; clear the ep8irq
1154         movx    @DPTR,a
1155
1156         pop     07h
1157         pop     06h
1158         pop     05h
1159         pop     04h             ; R4
1160         pop     03h             ; R3
1161         pop     02h             ; R2
1162         pop     01h             ; R1
1163         pop     00h             ; R0
1164         pop     psw
1165         pop     acc 
1166         pop     dph1 
1167         pop     dpl1
1168         pop     dph 
1169         pop     dpl 
1170         pop     dps
1171         reti
1172
1173
1174
1175 ;;; GPIF waveform for PWM
1176 waveform:
1177         ;;      0     1     2     3     4     5     6     7(not used)
1178         ;; len (gives 50.007Hz)
1179         .db     195,  195,  195,  195,  195,  195,  1,    1
1180
1181         ;; opcode
1182         .db     002H, 006H, 002H, 002H, 002H, 002H, 002H, 002H
1183         
1184         ;; out
1185         .db     0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH, 0ffH
1186
1187         ;; log
1188         .db     000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H
1189
1190
1191 stopPWM:
1192         mov     r0,#PWMFLAG     ; flag for PWM
1193         mov     a,#0            ; PWM (for the main loop)
1194         mov     @r0,a           ; set it
1195
1196         mov     dptr,#IFCONFIG  ; switch off GPIF
1197         mov     a,#10100000b    ; gpif, 30MHz, internal IFCLK
1198         lcall   syncdelaywr
1199         ret
1200         
1201
1202 ;;; init PWM
1203 startPWM:
1204         mov     dptr,#IFCONFIG  ; switch on IFCLK signal
1205         mov     a,#10100010b    ; gpif, 30MHz, internal IFCLK
1206         lcall   syncdelaywr
1207
1208         mov     OEB,0FFH        ; output to port B
1209
1210         mov     DPTR,#EP4CFG
1211         mov     a,#10100000b    ; valid, out, bulk
1212         movx    @DPTR,a
1213
1214         ;; reset the endpoint
1215         mov     dptr,#FIFORESET
1216         mov     a,#80h          ; NAK
1217         lcall   syncdelaywr
1218         mov     a,#84h          ; reset EP4 + NAK
1219         lcall   syncdelaywr
1220         mov     a,#0            ; normal op
1221         lcall   syncdelaywr
1222
1223         mov     dptr,#EP4BCL
1224         mov     a,#0H           ; discard packets
1225         lcall   syncdelaywr     ; empty FIFO buffer
1226         lcall   syncdelaywr     ; empty FIFO buffer
1227
1228         ;; aborts all transfers by the GPIF
1229         mov     dptr,#GPIFABORT
1230         mov     a,#0ffh         ; abort all transfers
1231         lcall   syncdelaywr
1232
1233         ;; wait for GPIF to finish
1234 wait_f_abort:
1235         mov     a,GPIFTRIG      ; GPIF status
1236         anl     a,#80h          ; done bit
1237         jz      wait_f_abort    ; GPIF busy
1238
1239         mov     dptr,#GPIFCTLCFG
1240         mov     a,#10000000b    ; tri state for CTRL
1241         lcall   syncdelaywr
1242
1243         mov     dptr,#GPIFIDLECTL
1244         mov     a,#11110000b    ; all CTL outputs low
1245         lcall   syncdelaywr
1246
1247         ;; abort if FIFO is empty
1248         mov     a,#00000001b    ; abort if empty
1249         mov     dptr,#EP4GPIFFLGSEL
1250         lcall   syncdelaywr
1251
1252         ;; 
1253         mov     a,#00000001b    ; stop if GPIF flg
1254         mov     dptr,#EP4GPIFPFSTOP
1255         lcall   syncdelaywr
1256
1257         ;; transaction counter
1258         mov     a,#0ffH
1259         mov     dptr,#GPIFTCB3
1260         lcall   syncdelaywr
1261
1262         ;; transaction counter
1263         mov     a,#0ffH
1264         mov     dptr,#GPIFTCB2
1265         lcall   syncdelaywr
1266
1267         ;; transaction counter
1268         mov     a,#0ffH         ; 512 bytes
1269         mov     dptr,#GPIFTCB1
1270         lcall   syncdelaywr
1271
1272         ;; transaction counter
1273         mov     a,#0ffH
1274         mov     dptr,#GPIFTCB0
1275         lcall   syncdelaywr
1276
1277         ;; RDY pins. Not used here.
1278         mov     a,#0
1279         mov     dptr,#GPIFREADYCFG
1280         lcall   syncdelaywr
1281
1282         ;; drives the output in the IDLE state
1283         mov     a,#1
1284         mov     dptr,#GPIFIDLECS
1285         lcall   syncdelaywr
1286
1287         ;; direct data transfer from the EP to the GPIF
1288         mov     dptr,#EP4FIFOCFG
1289         mov     a,#00010000b    ; autoout=1, byte-wide
1290         lcall   syncdelaywr
1291
1292         ;; waveform 0 is used for FIFO out
1293         mov     dptr,#GPIFWFSELECT
1294         mov     a,#00000000b
1295         movx    @dptr,a
1296         lcall   syncdelay
1297
1298         ;; transfer the delay byte from the EP to the waveform
1299         mov     dptr,#0e781h    ; EP1 buffer
1300         movx    a,@dptr         ; get the delay
1301         mov     dptr,#waveform  ; points to the waveform
1302         mov     r2,#6           ; fill 6 bytes
1303 timloop:
1304         movx    @dptr,a         ; save timing in a xxx
1305         inc     dptr
1306         djnz    r2,timloop      ; fill the 6 delay bytes
1307
1308         ;; load waveform
1309         mov     AUTOPTRH2,#0E4H ; XDATA0H
1310         lcall   syncdelay
1311         mov     AUTOPTRL2,#00H  ; XDATA0L
1312         lcall   syncdelay
1313
1314         mov     dptr,#waveform  ; points to the waveform
1315         
1316         mov     AUTOPTRSETUP,#7 ; autoinc and enable
1317         lcall   syncdelay
1318
1319         mov     r2,#20H         ; 32 bytes to transfer
1320
1321 wavetr:
1322         movx    a,@dptr
1323         inc     dptr
1324         push    dpl
1325         push    dph
1326         push    dpl1
1327         push    dph1
1328         mov     dptr,#XAUTODAT2
1329         movx    @dptr,a
1330         lcall   syncdelay
1331         pop     dph1 
1332         pop     dpl1
1333         pop     dph 
1334         pop     dpl
1335         djnz    r2,wavetr
1336
1337         mov     dptr,#OUTPKTEND
1338         mov     a,#084H
1339         lcall   syncdelaywr
1340         lcall   syncdelaywr
1341
1342         mov     r0,#PWMFLAG     ; flag for PWM
1343         mov     a,#1            ; PWM (for the main loop)
1344         mov     @r0,a           ; set it
1345
1346         ret
1347
1348         
1349
1350 ;; need to delay every time the byte counters
1351 ;; for the EPs have been changed.
1352
1353 syncdelay:
1354         nop
1355         nop
1356         nop
1357         nop
1358         nop
1359         nop
1360         nop
1361         nop
1362         nop
1363         ret
1364
1365 syncdelaywr:
1366         movx    @dptr,a
1367         lcall   syncdelay
1368         ret
1369
1370
1371
1372         .org    1F00h           ; lookup table at the end of memory
1373
1374 swap_lut:
1375 .db 0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136
1376 .db 72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100
1377 .db 228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220
1378 .db 60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10
1379 .db 138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166
1380 .db 102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94
1381 .db 222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9
1382 .db 137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165
1383 .db 101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93
1384 .db 221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11
1385 .db 139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167
1386 .db 103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95
1387 .db 223,63,191,127,255
1388
1389
1390
1391         
1392 .End
1393
1394