1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114#define HIGH(x) (((x) & 0xff00) / 256)
115#define LOW(x) ((x) & 0xff)
116
117#define dpl1 0x84
118#define dph1 0x85
119#define dps 0x86
120
121;;; our bit assignments
122#define TX_RUNNING 0
123#define DO_TX_UNTHROTTLE 1
124
125 ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
126#define STACK
127
128#define EXIF 0x91
129#define EIE 0xe8
130 .flag EUSB, EIE.0
131 .flag ES0, IE.4
132
133#define EP0CS
134#define EP0STALLbit
135#define IN0BUF
136#define IN0BC
137#define OUT0BUF
138#define OUT0BC
139#define IN2BUF
140#define IN2BC
141#define IN2CS
142#define OUT2BC
143#define OUT2CS
144#define OUT2BUF
145#define IN4BUF
146#define IN4BC
147#define IN4CS
148#define OEB
149#define OUTB
150#define OEC
151#define OUTC
152#define PINSC
153#define PORTBCFG
154#define PORTCCFG
155#define OEA
156#define IN07IRQ
157#define OUT07IRQ
158#define IN07IEN
159#define OUT07IEN
160#define USBIRQ
161#define USBIEN
162#define USBBAV
163#define USBCS
164#define SUDPTRH
165#define SUDPTRL
166#define SETUPDAT
167
168 ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
169
170 .org 0
171 ljmp start
172 ;; interrupt vectors
173 .org 23H
174 ljmp serial_int
175 .byte 0
176
177 .org 43H
178 ljmp USB_Jump_Table
179 .byte 0 ; filled in by the USB core
180
181;;; local variables. These are not initialized properly: do it by hand.
182 .org 30H
183rx_ring_in: .byte 0
184rx_ring_out: .byte 0
185tx_ring_in: .byte 0
186tx_ring_out: .byte 0
187tx_unthrottle_threshold: .byte 0
188
189 .org 0x100H ; wants to be on a page boundary
190USB_Jump_Table:
191 ljmp ISR_Sudav ; Setup Data Available
192 .byte 0
193 ljmp 0 ; Start of Frame
194 .byte 0
195 ljmp 0 ; Setup Data Loading
196 .byte 0
197 ljmp 0 ; Global Suspend
198 .byte 0
199 ljmp 0 ; USB Reset
200 .byte 0
201 ljmp 0 ; Reserved
202 .byte 0
203 ljmp 0 ; End Point 0 In
204 .byte 0
205 ljmp 0 ; End Point 0 Out
206 .byte 0
207 ljmp 0 ; End Point 1 In
208 .byte 0
209 ljmp 0 ; End Point 1 Out
210 .byte 0
211 ljmp ISR_Ep2in
212 .byte 0
213 ljmp ISR_Ep2out
214 .byte 0
215
216
217 .org 0x200
218
219start: mov SP,STACK-1 ; set stack
220 ;; clear local variables
221 clr a
222 mov tx_ring_in, a
223 mov tx_ring_out, a
224 mov rx_ring_in, a
225 mov rx_ring_out, a
226 mov tx_unthrottle_threshold, a
227 clr TX_RUNNING
228 clr DO_TX_UNTHROTTLE
229
230 ;; clear fifo with "fe"
231 mov r1, 0
232 mov a,
233 mov dptr,
234clear_tx_ring_loop:
235 movx @dptr, a
236 inc dptr
237 djnz r1, clear_tx_ring_loop
238
239 mov a,
240 mov dptr,
241clear_rx_ring_loop:
242 movx @dptr, a
243 inc dptr
244 djnz r1, clear_rx_ring_loop
245
246;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
247;;; on Xircom the STANDBY is wired to PB6 and PC4
248 mov dptr, PORTBCFG
249 mov a,
250 movx @dptr, a
251 mov dptr, PORTCCFG
252 mov a,
253 movx @dptr, a
254
255 ;; set OEC.4
256 mov a,
257 mov dptr,OEC
258 movx @dptr,a
259
260 ;; clear PC4
261 mov a,
262 mov dptr,OUTC
263 movx @dptr,a
264
265 ;; set OEB.6
266 mov a,
267 mov dptr,OEB
268 movx @dptr,a
269
270 ;; clear PB6
271 mov a,
272 mov dptr,OUTB
273 movx @dptr,a
274
275 ;; set OEC.[17]
276 mov a,
277 mov dptr,OEC
278 movx @dptr,a
279
280
281 ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
282 mov dptr, PORTCCFG
283 mov a,
284 movx @dptr, a
285
286 ;; set up interrupts, autovectoring
287 ;; set BKPT
288 mov dptr, USBBAV
289 movx a,@dptr
290 setb acc.0 ; AVEN bit to 0
291 movx @dptr, a
292
293 mov a,
294 mov dptr, USBIRQ
295 movx @dptr, a ; clear SUDAVI
296 mov dptr, USBIEN
297 movx @dptr, a
298
299 mov dptr, IN07IEN
300 mov a,
301 movx @dptr, a
302
303 mov dptr, OUT07IEN
304 mov a,
305 movx @dptr, a
306 mov dptr, OUT2BC
307 movx @dptr, a ; arm OUT2
308
309;; mov a,
310;; mov dptr,OUTC
311;; movx @dptr, a
312
313 mov a,
314 mov dptr,USBBAV
315 movx @dptr, a
316
317 mov a,
318 mov dptr,OEA
319 movx @dptr, a
320
321 mov a,
322 mov dptr,OUTC
323 movx @dptr, a
324
325 ;; setup the serial port. 9600 8N1.
326 mov a,
327 mov SCON, a
328 ;; using timer2, in 16-bit baud-rate-generator mode
329 ;; (xtal 12MHz, internal fosc 24MHz)
330 ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud)
331 ;; 57600: 0xFFF2.F, say 0xFFF3
332 ;; 9600: 0xFFB1.E, say 0xFFB2
333 ;; 300: 0xF63C
334#define BAUD 9600
335#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate))
336#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate))
337#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate))
338
339 mov T2CON,
340 mov r3,
341 acall set_baud
342 setb TR2
343 mov SCON,
344
345
346 mov r1,
347 mov a,
348send:
349 mov SBUF, a
350 inc a
351 anl a,
352 orl a,
353; xrl a,
354wait1:
355 jnb TI, wait1
356 clr TI
357 djnz r1, send
358;done: sjmp done
359
360#endif
361
362 setb EUSB
363 setb EA
364 setb ES0
365 ;acall dump_stat
366
367 ;; hey, what say we RENUMERATE! (TRM p.62)
368 mov a,
369 mov dps, a
370 mov dptr, USBCS
371 mov a,
372 movx @dptr, a
373 ;; now presence pin is floating, simulating disconnect. wait 0.5s
374 mov r1,
375renum_wait1:
376 mov r2,
377renum_wait2:
378 mov r3,
379renum_wait3:
380 djnz r3, renum_wait3
381 djnz r2, renum_wait2
382 djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
383 mov a,
384 movx @dptr, a
385 ;; we are back online. the host device will now re-query us
386
387
388main: sjmp main
389
390
391
392ISR_Sudav:
393 push dps
394 push dpl
395 push dph
396 push dpl1
397 push dph1
398 push acc
399 mov a,EXIF
400 clr acc.4
401 mov EXIF,a ; clear INT2 first
402 mov dptr, USBIRQ ; clear USB int
403 mov a,
404 movx @dptr,a
405
406 ;; get request type
407 mov dptr, SETUPDAT
408 movx a, @dptr
409 mov r1, a ; r1 = bmRequestType
410 inc dptr
411 movx a, @dptr
412 mov r2, a ; r2 = bRequest
413 inc dptr
414 movx a, @dptr
415 mov r3, a ; r3 = wValueL
416 inc dptr
417 movx a, @dptr
418 mov r4, a ; r4 = wValueH
419
420 ;; main switch on bmRequest.type: standard or vendor
421 mov a, r1
422 anl a,
423 cjne a,
424 ;; standard request: now main switch is on bRequest
425 ljmp setup_bmreq_is_standard
426
427setup_bmreq_type_not_standard:
428 ;; a still has bmreq&0x60
429 cjne a,
430 ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
431 ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
432 cjne r2,
433 ;; 00 is set baud, wValue[0] has baud rate index
434 lcall set_baud ; index in r3, carry set if error
435 jc setup_bmreq_type_not_standard__do_stall
436 ljmp setup_done_ack
437setup_bmreq_type_not_standard__do_stall:
438 ljmp setup_stall
439setup_ctrl_not_00:
440 cjne r2,
441 ;; 01 is reserved for set bits (parity). TODO
442 ljmp setup_stall
443setup_ctrl_not_01:
444 cjne r2,
445 ;; 02 is set HW flow control. TODO
446 ljmp setup_stall
447setup_ctrl_not_02:
448 cjne r2,
449 ;; 03 is control pins (RTS, DTR).
450 ljmp control_pins ; will jump to setup_done_ack,
451 ; or setup_return_one_byte
452setup_ctrl_not_03:
453 cjne r2,
454 ;; 04 is send break (really "turn break on/off"). TODO
455 cjne r3,
456 ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
457 mov dptr, PORTCCFG
458 movx a, @dptr
459 orl a,
460 movx @dptr, a
461 ljmp setup_done_ack
462setup_ctrl_do_break_on:
463 ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
464 mov dptr, OUTC
465 movx a, @dptr
466 anl a,
467 movx @dptr, a
468 mov dptr, PORTCCFG
469 movx a, @dptr
470 anl a,
471 movx @dptr, a
472 ljmp setup_done_ack
473setup_ctrl_not_04:
474 cjne r2,
475 ;; 05 is set desired interrupt bitmap. TODO
476 ljmp setup_stall
477setup_ctrl_not_05:
478 cjne r2,
479 ;; 06 is query room
480 cjne r3,
481 ;; 06, wValue[0]=0 is query write_room
482 mov a, tx_ring_out
483 setb c
484 subb a, tx_ring_in ; out-1-in = 255 - (in-out)
485 ljmp setup_return_one_byte
486setup_ctrl_06_not_00:
487 cjne r3,
488 ;; 06, wValue[0]=1 is query chars_in_buffer
489 mov a, tx_ring_in
490 clr c
491 subb a, tx_ring_out ; in-out
492 ljmp setup_return_one_byte
493setup_ctrl_06_not_01:
494 ljmp setup_stall
495setup_ctrl_not_06:
496 cjne r2,
497 ;; 07 is request tx unthrottle interrupt
498 mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
499 ljmp setup_done_ack
500setup_ctrl_not_07:
501 ljmp setup_stall
502
503setup_bmreq_type_not_vendor:
504 ljmp setup_stall
505
506
507setup_bmreq_is_standard:
508 cjne r2,
509 ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
510 cjne r1,
511 ;; Get_Status(device)
512 ;; are we self-powered? no. can we do remote wakeup? no
513 ;; so return two zero bytes. This is reusable
514setup_return_two_zero_bytes:
515 mov dptr, IN0BUF
516 clr a
517 movx @dptr, a
518 inc dptr
519 movx @dptr, a
520 mov dptr, IN0BC
521 mov a,
522 movx @dptr, a
523 ljmp setup_done_ack
524setup_Get_Status_not_device:
525 cjne r1,
526 ;; Get_Status(endpoint)
527 ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0
528 ;; for now: cheat. TODO
529 sjmp setup_return_two_zero_bytes
530setup_Get_Status_not_endpoint:
531 cjne r1,
532 ;; Get_Status(interface): return two zeros
533 sjmp setup_return_two_zero_bytes
534setup_Get_Status_not_interface:
535 ljmp setup_stall
536
537setup_breq_not_00:
538 cjne r2,
539 ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
540 cjne r3,
541 ;; Clear_Feature(stall). should clear a stall bit. TODO
542 ljmp setup_stall
543setup_Clear_Feature_not_stall:
544 cjne r3,
545 ;; Clear_Feature(remote wakeup). ignored.
546 ljmp setup_done_ack
547setup_Clear_Feature_not_rwake:
548 ljmp setup_stall
549
550setup_breq_not_01:
551 cjne r2,
552 ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
553 cjne r3,
554 ;; Set_Feature(stall). Should set a stall bit. TODO
555 ljmp setup_stall
556setup_Set_Feature_not_stall:
557 cjne r3,
558 ;; Set_Feature(remote wakeup). ignored.
559 ljmp setup_done_ack
560setup_Set_Feature_not_rwake:
561 ljmp setup_stall
562
563setup_breq_not_03:
564 cjne r2,
565 ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
566 cjne r4,
567 ;; Get_Descriptor(device)
568 mov dptr, SUDPTRH
569 mov a,
570 movx @dptr, a
571 mov dptr, SUDPTRL
572 mov a,
573 movx @dptr, a
574 ljmp setup_done_ack
575setup_Get_Descriptor_not_device:
576 cjne r4,
577 ;; Get_Descriptor(config[n])
578 cjne r3,
579 ;; Get_Descriptor(config[0])
580 mov dptr, SUDPTRH
581 mov a,
582 movx @dptr, a
583 mov dptr, SUDPTRL
584 mov a,
585 movx @dptr, a
586 ljmp setup_done_ack
587setup_Get_Descriptor_not_config:
588 cjne r4,
589 ;; Get_Descriptor(string[wValueL])
590 ;; if (wValueL >= maxstrings) stall
591 mov a,
592 clr c
593 subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
594 jc setup_stall
595 jz setup_stall
596 mov a, r3
597 add a, r3 ; a = 2*wValueL
598 mov dptr,
599 add a, dpl
600 mov dpl, a
601 mov a,
602 addc a, dph
603 mov dph, a ; dph = desc_strings[a]. big endian! (handy)
604 ;; it looks like my adapter uses a revision of the EZUSB that
605 ;; contains "rev D errata number 8", as hinted in the EzUSB example
606 ;; code. I cannot find an actual errata description on the Cypress
607 ;; web site, but from the example code it looks like this bug causes
608 ;; the length of string descriptors to be read incorrectly, possibly
609 ;; sending back more characters than the descriptor has. The workaround
610 ;; is to manually send out all of the data. The consequence of not
611 ;; using the workaround is that the strings gathered by the kernel
612 ;; driver are too long and are filled with trailing garbage (including
613 ;; leftover strings). Writing this out by hand is a nuisance, so for
614 ;; now I will just live with the bug.
615 movx a, @dptr
616 mov r1, a
617 inc dptr
618 movx a, @dptr
619 mov r2, a
620 mov dptr, SUDPTRH
621 mov a, r1
622 movx @dptr, a
623 mov dptr, SUDPTRL
624 mov a, r2
625 movx @dptr, a
626 ;; done
627 ljmp setup_done_ack
628
629setup_Get_Descriptor_not_string:
630 ljmp setup_stall
631
632setup_breq_not_06:
633 cjne r2,
634 ;; Get_Configuration. always 1. return one byte.
635 ;; this is reusable
636 mov a,
637setup_return_one_byte:
638 mov dptr, IN0BUF
639 movx @dptr, a
640 mov a,
641 mov dptr, IN0BC
642 movx @dptr, a
643 ljmp setup_done_ack
644setup_breq_not_08:
645 cjne r2,
646 ;; 09: Set_Configuration. ignored.
647 ljmp setup_done_ack
648setup_breq_not_09:
649 cjne r2,
650 ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
651 ;; since we only have one interface, ignore wIndexL, return a 0
652 mov a,
653 ljmp setup_return_one_byte
654setup_breq_not_0a:
655 cjne r2,
656 ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
657 ljmp setup_done_ack
658setup_breq_not_0b:
659 ljmp setup_stall
660
661
662setup_done_ack:
663 ;; now clear HSNAK
664 mov dptr, EP0CS
665 mov a,
666 movx @dptr, a
667 sjmp setup_done
668setup_stall:
669 ;; unhandled. STALL
670 ;EP0CS |= bmEPSTALL
671 mov dptr, EP0CS
672 movx a, @dptr
673 orl a, EP0STALLbit
674 movx @dptr, a
675 sjmp setup_done
676
677setup_done:
678 pop acc
679 pop dph1
680 pop dpl1
681 pop dph
682 pop dpl
683 pop dps
684 reti
685
686;;; ==============================================================
687
688set_baud: ; baud index in r3
689 ;; verify a < 10
690 mov a, r3
691 jb ACC.7, set_baud__badbaud
692 clr c
693 subb a,
694 jnc set_baud__badbaud
695 mov a, r3
696 rl a ; a = index*2
697 add a,
698 mov dpl, a
699 mov a,
700 addc a,
701 mov dph, a
702 ;; TODO: shut down xmit/receive
703 ;; TODO: wait for current xmit char to leave
704 ;; TODO: shut down timer to avoid partial-char glitch
705 movx a,@dptr ; BAUD_HIGH
706 mov RCAP2H, a
707 mov TH2, a
708 inc dptr
709 movx a,@dptr ; BAUD_LOW
710 mov RCAP2L, a
711 mov TL2, a
712 ;; TODO: restart xmit/receive
713 ;; TODO: reenable interrupts, resume tx if pending
714 clr c ; c=0: success
715 ret
716set_baud__badbaud:
717 setb c ; c=1: failure
718 ret
719
720;;; ==================================================
721control_pins:
722 cjne r1,
723control_pins_out:
724 ;TODO BKPT is DTR
725 mov a, r3 ; wValue[0] holds new bits: b7 is new RTS
726 xrl a,
727 anl a,
728 mov r3, a
729 mov dptr, OUTC
730 movx a, @dptr ; only change bit 7
731 anl a,
732 orl a, r3
733 movx @dptr, a ; other pins are inputs, bits ignored
734 ljmp setup_done_ack
735control_pins_in:
736 mov dptr, PINSC
737 movx a, @dptr
738 xrl a,
739 ljmp setup_return_one_byte
740
741;;; ========================================
742
743ISR_Ep2in:
744 push dps
745 push dpl
746 push dph
747 push dpl1
748 push dph1
749 push acc
750 mov a,EXIF
751 clr acc.4
752 mov EXIF,a ; clear INT2 first
753 mov dptr, IN07IRQ ; clear USB int
754 mov a,
755 movx @dptr,a
756
757 mov a,
758 mov dptr,OEA
759 movx @dptr, a
760
761
762 ;; do stuff
763 lcall start_in
764
765 mov a,
766 mov dptr,OEA
767 movx @dptr, a
768
769
770
771 pop acc
772 pop dph1
773 pop dpl1
774 pop dph
775 pop dpl
776 pop dps
777 reti
778
779ISR_Ep2out:
780 push dps
781 push dpl
782 push dph
783 push dpl1
784 push dph1
785 push acc
786
787 mov a,
788 mov dptr,OEA
789 movx @dptr, a
790
791
792
793 mov a,EXIF
794 clr acc.4
795 mov EXIF,a ; clear INT2 first
796 mov dptr, OUT07IRQ ; clear USB int
797 mov a,
798 movx @dptr,a
799
800 ;; do stuff
801
802 ;; copy data into buffer. for now, assume we will have enough space
803 mov dptr, OUT2BC ; get byte count
804 movx a,@dptr
805 mov r1, a
806 clr a
807 mov dps, a
808 mov dptr, OUT2BUF ; load DPTR0 with source
809 mov dph1,
810 mov dpl1, tx_ring_in
811OUT_loop:
812 movx a,@dptr ; read
813 inc dps ; switch to DPTR1: target
814 inc dpl1 ; target = tx_ring_in+1
815 movx @dptr,a ; store
816 mov a,dpl1
817 cjne a, tx_ring_out, OUT_no_overflow
818 sjmp OUT_overflow
819OUT_no_overflow:
820 inc tx_ring_in ; tx_ring_in++
821 inc dps ; switch to DPTR0: source
822 inc dptr
823 djnz r1, OUT_loop
824 sjmp OUT_done
825OUT_overflow:
826 ;; signal overflow
827 ;; fall through
828OUT_done:
829 ;; ack
830 mov dptr,OUT2BC
831 movx @dptr,a
832
833 ;; start tx
834 acall maybe_start_tx
835 ;acall dump_stat
836
837 mov a,
838 mov dptr,OEA
839 movx @dptr, a
840
841 pop acc
842 pop dph1
843 pop dpl1
844 pop dph
845 pop dpl
846 pop dps
847 reti
848
849dump_stat:
850 ;; fill in EP4in with a debugging message:
851 ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
852 ;; tx_active
853 ;; tx_ring[0..15]
854 ;; 0xfc
855 ;; rx_ring[0..15]
856 clr a
857 mov dps, a
858
859 mov dptr, IN4CS
860 movx a, @dptr
861 jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
862 mov dptr, IN4BUF
863
864 mov a, tx_ring_in
865 movx @dptr, a
866 inc dptr
867 mov a, tx_ring_out
868 movx @dptr, a
869 inc dptr
870
871 mov a, rx_ring_in
872 movx @dptr, a
873 inc dptr
874 mov a, rx_ring_out
875 movx @dptr, a
876 inc dptr
877
878 clr a
879 jnb TX_RUNNING, dump_stat__no_tx_running
880 inc a
881dump_stat__no_tx_running:
882 movx @dptr, a
883 inc dptr
884 ;; tx_ring[0..15]
885 inc dps
886 mov dptr,
887 mov r1,
888dump_stat__tx_ring_loop:
889 movx a, @dptr
890 inc dptr
891 inc dps
892 movx @dptr, a
893 inc dptr
894 inc dps
895 djnz r1, dump_stat__tx_ring_loop
896 inc dps
897
898 mov a,
899 movx @dptr, a
900 inc dptr
901
902 ;; rx_ring[0..15]
903 inc dps
904 mov dptr,
905 mov r1,
906dump_stat__rx_ring_loop:
907 movx a, @dptr
908 inc dptr
909 inc dps
910 movx @dptr, a
911 inc dptr
912 inc dps
913 djnz r1, dump_stat__rx_ring_loop
914
915 ;; now send it
916 clr a
917 mov dps, a
918 mov dptr, IN4BC
919 mov a,
920 movx @dptr, a
921dump_stat__done:
922 ret
923
924;;; ============================================================
925
926maybe_start_tx:
927 ;; make sure the tx process is running.
928 jb TX_RUNNING, start_tx_done
929start_tx:
930 ;; is there work to be done?
931 mov a, tx_ring_in
932 cjne a,tx_ring_out, start_tx__work
933 ret ; no work
934start_tx__work:
935 ;; tx was not running. send the first character, setup the TI int
936 inc tx_ring_out ; [++tx_ring_out]
937 mov dph,
938 mov dpl, tx_ring_out
939 movx a, @dptr
940 mov sbuf, a
941 setb TX_RUNNING
942start_tx_done:
943 ;; can we unthrottle the host tx process?
944 ;; step 1: do we care?
945 mov a,
946 cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
947 ;; nope
948start_tx_really_done:
949 ret
950start_tx__maybe_unthrottle_tx:
951 ;; step 2: is there now room?
952 mov a, tx_ring_out
953 setb c
954 subb a, tx_ring_in
955 ;; a is now write_room. If thresh >= a, we can unthrottle
956 clr c
957 subb a, tx_unthrottle_threshold
958 jc start_tx_really_done ; nope
959 ;; yes, we can unthrottle. remove the threshold and mark a request
960 mov tx_unthrottle_threshold,
961 setb DO_TX_UNTHROTTLE
962 ;; prod rx, which will actually send the message when in2 becomes free
963 ljmp start_in
964
965
966serial_int:
967 push dps
968 push dpl
969 push dph
970 push dpl1
971 push dph1
972 push acc
973 jnb TI, serial_int__not_tx
974 ;; tx finished. send another character if we have one
975 clr TI ; clear int
976 clr TX_RUNNING
977 lcall start_tx
978serial_int__not_tx:
979 jnb RI, serial_int__not_rx
980 lcall get_rx_char
981 clr RI ; clear int
982serial_int__not_rx:
983 ;; return
984 pop acc
985 pop dph1
986 pop dpl1
987 pop dph
988 pop dpl
989 pop dps
990 reti
991
992get_rx_char:
993 mov dph,
994 mov dpl, rx_ring_in
995 inc dpl ; target = rx_ring_in+1
996 mov a, sbuf
997 movx @dptr, a
998 ;; check for overflow before incrementing rx_ring_in
999 mov a, dpl
1000 cjne a, rx_ring_out, get_rx_char__no_overflow
1001 ;; signal overflow
1002 ret
1003get_rx_char__no_overflow:
1004 inc rx_ring_in
1005 ;; kick off USB INpipe
1006 acall start_in
1007 ret
1008
1009start_in:
1010 ;; check if the inpipe is already running.
1011 mov a,
1012 mov dptr, OEA
1013 movx @dptr,a
1014
1015 mov dptr, IN2CS
1016 movx a, @dptr
1017 jb acc.1, start_in__done; int will handle it
1018 jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle
1019 ;; see if there is any work to do. a serial interrupt might occur
1020 ;; during this sequence?
1021 mov a, rx_ring_in
1022 cjne a, rx_ring_out, start_in__have_work
1023 ret ; nope
1024start_in__have_work:
1025 ;; now copy as much data as possible into the pipe. 63 bytes max.
1026 clr a
1027 mov dps, a
1028 mov dph,
1029 inc dps
1030 mov dptr, IN2BUF ; load DPTR1 with target
1031 movx @dptr, a ; in[0] signals that rest of IN is rx data
1032 inc dptr
1033 inc dps
1034 ;; loop until we run out of data, or we have copied 64 bytes
1035 mov r1,
1036start_in__loop:
1037 mov a, rx_ring_in
1038 cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
1039 sjmp start_in__kick
1040start_inlocal_irq_enablell_copying:
1041 inc rx_ring_out
1042 mov dpl, rx_ring_out
1043 movx a, @dptr
1044 inc dps
1045 movx @dptr, a ; write into IN buffer
1046 inc dptr
1047 inc dps
1048 inc r1
1049 cjne r1,
1050start_in__kick:
1051 ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
1052 ;; kick off IN
1053 mov a,
1054 mov dptr,OEA
1055 movx @dptr, a
1056 mov dptr, IN2BC
1057 mov a, r1
1058 jz start_in__done
1059 movx @dptr, a
1060 ;; done
1061start_in__done:
1062 ;acall dump_stat
1063 ret
1064start_in__do_tx_unthrottle:
1065 ;; special sequence: send a tx unthrottle message
1066 clr DO_TX_UNTHROTTLE
1067 clr a
1068 mov dps, a
1069 mov dptr, IN2BUF
1070 mov a,
1071 movx @dptr, a
1072 inc dptr
1073 mov a,
1074 movx @dptr, a
1075 mov dptr, IN2BC
1076 movx @dptr, a
1077 ret
1078
1079putchar:
1080 clr TI
1081 mov SBUF, a
1082putchar_wait:
1083 jnb TI, putchar_wait
1084 clr TI
1085 ret
1086
1087
1088baud_table: ; baud_high, then baud_low
1089 ;; baud[0]: 110
1090 .byte BAUD_HIGH(110)
1091 .byte BAUD_LOW(110)
1092 ;; baud[1]: 300
1093 .byte BAUD_HIGH(300)
1094 .byte BAUD_LOW(300)
1095 ;; baud[2]: 1200
1096 .byte BAUD_HIGH(1200)
1097 .byte BAUD_LOW(1200)
1098 ;; baud[3]: 2400
1099 .byte BAUD_HIGH(2400)
1100 .byte BAUD_LOW(2400)
1101 ;; baud[4]: 4800
1102 .byte BAUD_HIGH(4800)
1103 .byte BAUD_LOW(4800)
1104 ;; baud[5]: 9600
1105 .byte BAUD_HIGH(9600)
1106 .byte BAUD_LOW(9600)
1107 ;; baud[6]: 19200
1108 .byte BAUD_HIGH(19200)
1109 .byte BAUD_LOW(19200)
1110 ;; baud[7]: 38400
1111 .byte BAUD_HIGH(38400)
1112 .byte BAUD_LOW(38400)
1113 ;; baud[8]: 57600
1114 .byte BAUD_HIGH(57600)
1115 .byte BAUD_LOW(57600)
1116 ;; baud[9]: 115200
1117 .byte BAUD_HIGH(115200)
1118 .byte BAUD_LOW(115200)
1119
1120desc_device:
1121 .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40
1122 .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01
1123;;; The "real" device id, which must match the host driver, is that
1124;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104
1125
1126desc_config1:
1127 .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32
1128 .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00
1129 .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01
1130 .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00
1131
1132desc_strings:
1133 .word string_langids, string_mfg, string_product, string_serial
1134desc_strings_end:
1135
1136string_langids: .byte string_langids_end-string_langids
1137 .byte 3
1138 .word 0
1139string_langids_end:
1140
1141 ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now
1142 ;; *that* is a pain in the ass to encode. And they are little-endian
1143 ;; too. Use this perl snippet to get the bytecodes:
1144
1145
1146
1147
1148
1149
1150
1151
1152string_mfg: .byte string_mfg_end-string_mfg
1153 .byte 3
1154; .byte "ACME usb widgets"
1155 .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00
1156string_mfg_end:
1157
1158string_product: .byte string_product_end-string_product
1159 .byte 3
1160; .byte "ACME USB serial widget"
1161 .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00
1162string_product_end:
1163
1164string_serial: .byte string_serial_end-string_serial
1165 .byte 3
1166; .byte "47"
1167 .byte 0x34, 0x00, 0x37, 0x00
1168string_serial_end:
1169
1170;;; ring buffer memory
1171 ;; tx_ring_in+1 is where the next input byte will go
1172 ;; [tx_ring_out] has been sent
1173 ;; if tx_ring_in == tx_ring_out, theres no work to do
1174 ;; there are (tx_ring_in - tx_ring_out) chars to be written
1175 ;; dont let _in lap _out
1176 ;; cannot inc if tx_ring_in+1 == tx_ring_out
1177 ;; write [tx_ring_in+1] then tx_ring_in++
1178 ;; if (tx_ring_in+1 == tx_ring_out), overflow
1179 ;; else tx_ring_in++
1180 ;; read/send [tx_ring_out+1], then tx_ring_out++
1181
1182 ;; rx_ring_in works the same way
1183
1184 .org 0x1000
1185tx_ring:
1186 .skip 0x100 ; 256 bytes
1187rx_ring:
1188 .skip 0x100 ; 256 bytes
1189
1190
1191 .END
1192
1193