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