1 /* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
3 * Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
4 * the EzUSB microcontroller.
6 * (C) Copyright 2000 Brian Warner <warner@lothar.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the
16 * This serial adapter is basically an EzUSB chip and an RS-232 line driver
17 * in a little widget that has a DB-9 on one end and a USB plug on the other.
18 * It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2
19 * as a baud-rate generator. The wiring is:
20 * PC0/RxD0 <- rxd (DB9 pin 2) PC4 <- dsr pin 6
21 * PC1/TxD0 -> txd pin 3 PC5 <- ri pin 9
22 * PC2 -> rts pin 7 PC6 <- dcd pin 1
23 * PC3 <- cts pin 8 PC7 -> dtr pin 4
24 * PB1 -> line driver standby
26 * The EzUSB register constants below come from their excellent documentation
27 * and sample code (which used to be available at www.anchorchips.com, but
28 * that has now been absorbed into Cypress' site and the CD-ROM contents
29 * don't appear to be available online anymore). If we get multiple
30 * EzUSB-based drivers into the kernel, it might be useful to pull them out
31 * into a separate .h file.
33 * THEORY OF OPERATION:
35 * There are two 256-byte ring buffers, one for tx, one for rx.
37 * EP2out is pure tx data. When it appears, the data is copied into the tx
38 * ring and serial transmission is started if it wasn't already running. The
39 * "tx buffer empty" interrupt may kick off another character if the ring
40 * still has data. If the host is tx-blocked because the ring filled up,
41 * it will request a "tx unthrottle" interrupt. If sending a serial character
42 * empties the ring below the desired threshold, we set a bit that will send
43 * up the tx unthrottle message as soon as the rx buffer becomes free.
45 * EP2in (interrupt) is used to send both rx chars and rx status messages
46 * (only "tx unthrottle" at this time) back up to the host. The first byte
47 * of the rx message indicates data (0) or status msg (1). Status messages
48 * are sent before any data.
50 * Incoming serial characters are put into the rx ring by the serial
51 * interrupt, and the EP2in buffer sent if it wasn't already in transit.
52 * When the EP2in buffer returns, the interrupt prompts us to send more
53 * rx chars (or status messages) if they are pending.
55 * Device control happens through "vendor specific" control messages on EP0.
56 * All messages are destined for the "Interface" (with the index always 0,
57 * so that if their two-port device might someday use similar firmware, we
58 * can use index=1 to refer to the second port). The messages defined are:
60 * bRequest = 0 : set baud/bits/parity
62 * 2 : reserved for setting HW flow control (CTSRTS)
63 * 3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc)
64 * 4 : set break (on/off)
65 * 5 : reserved for requesting interrupts on pin state change
66 * 6 : query buffer room or chars in tx buffer
67 * 7 : request tx unthrottle interrupt
69 * The host-side driver is set to recognize the device ID values stashed in
70 * serial EEPROM (0x06cd, 0x0103), program this firmware into place, then
71 * start it running. This firmware will use EzUSB's "renumeration" trick by
72 * simulating a bus disconnect, then reconnect with a different device ID
73 * (encoded in the desc_device descriptor below). The host driver then
74 * recognizes the new device ID and glues it to the real serial driver code.
77 * EzUSB Technical Reference Manual: <http://www.cypress.com/>
78 * 8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is
79 * basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports
80 * use totally different registers!
81 * USB 1.1 spec: www.usb.org
84 * gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s
85 * as31 -l keyspan_pda.asm
86 * mv keyspan_pda.obj keyspan_pda.hex
87 * perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h
88 * Get as31 from <http://www.pjrc.com/tech/8051/index.html>, and hack on it
89 * a bit to make it build.
92 * Greg Kroah-Hartman, for coordinating the whole usb-serial thing.
93 * AnchorChips, for making such an incredibly useful little microcontroller.
94 * KeySpan, for making a handy, cheap ($40) widget that was so easy to take
95 * apart and trace with an ohmmeter.
98 * lots. grep for TODO. Interrupt safety needs stress-testing. Better flow
99 * control. Interrupting host upon change in DCD, etc, counting transitions.
100 * Need to find a safe device id to use (the one used by the Keyspan firmware
101 * under Windows would be ideal.. can anyone figure out what it is?). Parity.
102 * More baud rates. Oh, and the string-descriptor-length silicon bug
103 * workaround should be implemented, but I'm lazy, and the consequence is
104 * that the device name strings that show up in your kernel log will have
105 * lots of trailing binary garbage in them (appears as ????). Device strings
106 * should be made more accurate.
108 * Questions, bugs, patches to Brian.
110 * -Brian Warner <warner@lothar.com>
114 #define HIGH(x) (((x) & 0xff00) / 256)
115 #define LOW(x) ((x) & 0xff)
121 ;;; our bit assignments
123 #define DO_TX_UNTHROTTLE 1
125 ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60
126 #define STACK #0x60-1
133 #define EP0CS #0x7fb4
134 #define EP0STALLbit #0x01
135 #define IN0BUF #0x7f00
136 #define IN0BC #0x7fb5
137 #define OUT0BUF #0x7ec0
138 #define OUT0BC #0x7fc5
139 #define IN2BUF #0x7e00
140 #define IN2BC #0x7fb9
141 #define IN2CS #0x7fb8
142 #define OUT2BC #0x7fc9
143 #define OUT2CS #0x7fc8
144 #define OUT2BUF #0x7dc0
145 #define IN4BUF #0x7d00
146 #define IN4BC #0x7fbd
147 #define IN4CS #0x7fbc
152 #define PINSC #0x7f9b
153 #define PORTBCFG #0x7f94
154 #define PORTCCFG #0x7f95
156 #define IN07IRQ #0x7fa9
157 #define OUT07IRQ #0x7faa
158 #define IN07IEN #0x7fac
159 #define OUT07IEN #0x7fad
160 #define USBIRQ #0x7fab
161 #define USBIEN #0x7fae
162 #define USBBAV #0x7faf
163 #define USBCS #0x7fd6
164 #define SUDPTRH #0x7fd4
165 #define SUDPTRL #0x7fd5
166 #define SETUPDAT #0x7fe8
168 ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91)
179 .byte 0 ; filled in by the USB core
181 ;;; local variables. These are not initialized properly: do it by hand.
187 tx_unthrottle_threshold: .byte 0
189 .org 0x100H ; wants to be on a page boundary
191 ljmp ISR_Sudav ; Setup Data Available
193 ljmp 0 ; Start of Frame
195 ljmp 0 ; Setup Data Loading
197 ljmp 0 ; Global Suspend
203 ljmp 0 ; End Point 0 In
205 ljmp 0 ; End Point 0 Out
207 ljmp 0 ; End Point 1 In
209 ljmp 0 ; End Point 1 Out
219 start: mov SP,STACK-1 ; set stack
220 ;; clear local variables
226 mov tx_unthrottle_threshold, a
230 ;; clear fifo with "fe"
237 djnz r1, clear_tx_ring_loop
244 djnz r1, clear_rx_ring_loop
246 ;;; turn on the RS-232 driver chip (bring the STANDBY pin low)
247 ;;; on Xircom the STANDBY is wired to PB6 and PC4
281 ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port
286 ;; set up interrupts, autovectoring
290 setb acc.0 ; AVEN bit to 0
293 mov a,#0x01 ; enable SUDAV: setup data available (for ep0)
295 movx @dptr, a ; clear SUDAVI
300 mov a,#0x04 ; enable IN2 int
304 mov a,#0x04 ; enable OUT2 int
307 movx @dptr, a ; arm OUT2
309 ;; mov a, #0x84 ; turn on RTS, DTR
313 mov a, #0x7 ; turn on DTR
317 mov a, #0x20 ; turn on the RED led
321 mov a, #0x80 ; turn on RTS
325 ;; setup the serial port. 9600 8N1.
326 mov a,#0x53 ; mode 1, enable rx, clear int
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
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))
339 mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later)
367 ;; hey, what say we RENUMERATE! (TRM p.62)
371 mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1
373 ;; now presence pin is floating, simulating disconnect. wait 0.5s
382 djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks
383 mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1
385 ;; we are back online. the host device will now re-query us
401 mov EXIF,a ; clear INT2 first
402 mov dptr, USBIRQ ; clear USB int
409 mov r1, a ; r1 = bmRequestType
412 mov r2, a ; r2 = bRequest
415 mov r3, a ; r3 = wValueL
418 mov r4, a ; r4 = wValueH
420 ;; main switch on bmRequest.type: standard or vendor
423 cjne a, #0x00, setup_bmreq_type_not_standard
424 ;; standard request: now main switch is on bRequest
425 ljmp setup_bmreq_is_standard
427 setup_bmreq_type_not_standard:
428 ;; a still has bmreq&0x60
429 cjne a, #0x40, setup_bmreq_type_not_vendor
430 ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones
431 ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1
432 cjne r2, #0x00, setup_ctrl_not_00
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
437 setup_bmreq_type_not_standard__do_stall:
440 cjne r2, #0x01, setup_ctrl_not_01
441 ;; 01 is reserved for set bits (parity). TODO
444 cjne r2, #0x02, setup_ctrl_not_02
445 ;; 02 is set HW flow control. TODO
448 cjne r2, #0x03, setup_ctrl_not_03
449 ;; 03 is control pins (RTS, DTR).
450 ljmp control_pins ; will jump to setup_done_ack,
451 ; or setup_return_one_byte
453 cjne r2, #0x04, setup_ctrl_not_04
454 ;; 04 is send break (really "turn break on/off"). TODO
455 cjne r3, #0x00, setup_ctrl_do_break_on
456 ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port
462 setup_ctrl_do_break_on:
463 ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low)
474 cjne r2, #0x05, setup_ctrl_not_05
475 ;; 05 is set desired interrupt bitmap. TODO
478 cjne r2, #0x06, setup_ctrl_not_06
480 cjne r3, #0x00, setup_ctrl_06_not_00
481 ;; 06, wValue[0]=0 is query write_room
484 subb a, tx_ring_in ; out-1-in = 255 - (in-out)
485 ljmp setup_return_one_byte
486 setup_ctrl_06_not_00:
487 cjne r3, #0x01, setup_ctrl_06_not_01
488 ;; 06, wValue[0]=1 is query chars_in_buffer
491 subb a, tx_ring_out ; in-out
492 ljmp setup_return_one_byte
493 setup_ctrl_06_not_01:
496 cjne r2, #0x07, setup_ctrl_not_07
497 ;; 07 is request tx unthrottle interrupt
498 mov tx_unthrottle_threshold, r3; wValue[0] is threshold value
503 setup_bmreq_type_not_vendor:
507 setup_bmreq_is_standard:
508 cjne r2, #0x00, setup_breq_not_00
509 ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int)
510 cjne r1, #0x80, setup_Get_Status_not_device
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
514 setup_return_two_zero_bytes:
524 setup_Get_Status_not_device:
525 cjne r1, #0x82, setup_Get_Status_not_endpoint
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
530 setup_Get_Status_not_endpoint:
531 cjne r1, #0x81, setup_Get_Status_not_interface
532 ;; Get_Status(interface): return two zeros
533 sjmp setup_return_two_zero_bytes
534 setup_Get_Status_not_interface:
538 cjne r2, #0x01, setup_breq_not_01
539 ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup)
540 cjne r3, #0x00, setup_Clear_Feature_not_stall
541 ;; Clear_Feature(stall). should clear a stall bit. TODO
543 setup_Clear_Feature_not_stall:
544 cjne r3, #0x01, setup_Clear_Feature_not_rwake
545 ;; Clear_Feature(remote wakeup). ignored.
547 setup_Clear_Feature_not_rwake:
551 cjne r2, #0x03, setup_breq_not_03
552 ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup)
553 cjne r3, #0x00, setup_Set_Feature_not_stall
554 ;; Set_Feature(stall). Should set a stall bit. TODO
556 setup_Set_Feature_not_stall:
557 cjne r3, #0x01, setup_Set_Feature_not_rwake
558 ;; Set_Feature(remote wakeup). ignored.
560 setup_Set_Feature_not_rwake:
564 cjne r2, #0x06, setup_breq_not_06
565 ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n])
566 cjne r4, #0x01, setup_Get_Descriptor_not_device
567 ;; Get_Descriptor(device)
569 mov a, #HIGH(desc_device)
572 mov a, #LOW(desc_device)
575 setup_Get_Descriptor_not_device:
576 cjne r4, #0x02, setup_Get_Descriptor_not_config
577 ;; Get_Descriptor(config[n])
578 cjne r3, #0x00, setup_stall; only handle n==0
579 ;; Get_Descriptor(config[0])
581 mov a, #HIGH(desc_config1)
584 mov a, #LOW(desc_config1)
587 setup_Get_Descriptor_not_config:
588 cjne r4, #0x03, setup_Get_Descriptor_not_string
589 ;; Get_Descriptor(string[wValueL])
590 ;; if (wValueL >= maxstrings) stall
591 mov a, #((desc_strings_end-desc_strings)/2)
593 subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall
597 add a, r3 ; a = 2*wValueL
598 mov dptr, #desc_strings
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.
629 setup_Get_Descriptor_not_string:
633 cjne r2, #0x08, setup_breq_not_08
634 ;; Get_Configuration. always 1. return one byte.
637 setup_return_one_byte:
645 cjne r2, #0x09, setup_breq_not_09
646 ;; 09: Set_Configuration. ignored.
649 cjne r2, #0x0a, setup_breq_not_0a
650 ;; 0a: Get_Interface. get the current altsetting for int[wIndexL]
651 ;; since we only have one interface, ignore wIndexL, return a 0
653 ljmp setup_return_one_byte
655 cjne r2, #0x0b, setup_breq_not_0b
656 ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored
686 ;;; ==============================================================
688 set_baud: ; baud index in r3
691 jb ACC.7, set_baud__badbaud
694 jnc set_baud__badbaud
697 add a, #LOW(baud_table)
699 mov a, #HIGH(baud_table)
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
709 movx a,@dptr ; BAUD_LOW
712 ;; TODO: restart xmit/receive
713 ;; TODO: reenable interrupts, resume tx if pending
717 setb c ; c=1: failure
720 ;;; ==================================================
722 cjne r1, #0x41, control_pins_in
725 mov a, r3 ; wValue[0] holds new bits: b7 is new RTS
726 xrl a, #0xff ; 1 means active, 0V, +12V ?
730 movx a, @dptr ; only change bit 7
733 movx @dptr, a ; other pins are inputs, bits ignored
739 ljmp setup_return_one_byte
741 ;;; ========================================
752 mov EXIF,a ; clear INT2 first
753 mov dptr, IN07IRQ ; clear USB int
757 mov a, #0x20 ; Turn off the green LED
765 mov a, #0x20 ; Turn off the green LED
787 mov a, #0x10 ; Turn the green LED
795 mov EXIF,a ; clear INT2 first
796 mov dptr, OUT07IRQ ; clear USB int
802 ;; copy data into buffer. for now, assume we will have enough space
803 mov dptr, OUT2BC ; get byte count
808 mov dptr, OUT2BUF ; load DPTR0 with source
809 mov dph1, #HIGH(tx_ring) ; load DPTR1 with target
813 inc dps ; switch to DPTR1: target
814 inc dpl1 ; target = tx_ring_in+1
817 cjne a, tx_ring_out, OUT_no_overflow
820 inc tx_ring_in ; tx_ring_in++
821 inc dps ; switch to DPTR0: source
837 mov a, #0x20 ; Turn off the green LED
850 ;; fill in EP4in with a debugging message:
851 ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out
861 jb acc.1, dump_stat__done; busy: cannot dump, old one still pending
879 jnb TX_RUNNING, dump_stat__no_tx_running
881 dump_stat__no_tx_running:
886 mov dptr, #tx_ring ; DPTR1: source
888 dump_stat__tx_ring_loop:
895 djnz r1, dump_stat__tx_ring_loop
904 mov dptr, #rx_ring ; DPTR1: source
906 dump_stat__rx_ring_loop:
913 djnz r1, dump_stat__rx_ring_loop
924 ;;; ============================================================
927 ;; make sure the tx process is running.
928 jb TX_RUNNING, start_tx_done
930 ;; is there work to be done?
932 cjne a,tx_ring_out, start_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, #HIGH(tx_ring)
943 ;; can we unthrottle the host tx process?
944 ;; step 1: do we care?
946 cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx
948 start_tx_really_done:
950 start_tx__maybe_unthrottle_tx:
951 ;; step 2: is there now room?
955 ;; a is now write_room. If thresh >= a, we can unthrottle
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, #0
961 setb DO_TX_UNTHROTTLE
962 ;; prod rx, which will actually send the message when in2 becomes free
973 jnb TI, serial_int__not_tx
974 ;; tx finished. send another character if we have one
979 jnb RI, serial_int__not_rx
993 mov dph, #HIGH(rx_ring)
995 inc dpl ; target = rx_ring_in+1
998 ;; check for overflow before incrementing rx_ring_in
1000 cjne a, rx_ring_out, get_rx_char__no_overflow
1003 get_rx_char__no_overflow:
1005 ;; kick off USB INpipe
1010 ;; check if the inpipe is already running.
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?
1022 cjne a, rx_ring_out, start_in__have_work
1024 start_in__have_work:
1025 ;; now copy as much data as possible into the pipe. 63 bytes max.
1028 mov dph, #HIGH(rx_ring) ; load DPTR0 with source
1030 mov dptr, IN2BUF ; load DPTR1 with target
1031 movx @dptr, a ; in[0] signals that rest of IN is rx data
1034 ;; loop until we run out of data, or we have copied 64 bytes
1035 mov r1, #1 ; INbuf size counter
1038 cjne a, rx_ring_out, start_inlocal_irq_enablell_copying
1040 start_inlocal_irq_enablell_copying:
1042 mov dpl, rx_ring_out
1045 movx @dptr, a ; write into IN buffer
1049 cjne r1, #64, start_in__loop; loop
1051 ;; either we ran out of data, or we copied 64 bytes. r1 has byte count
1053 mov a, #0x10 ; Turn the green LED
1064 start_in__do_tx_unthrottle:
1065 ;; special sequence: send a tx unthrottle message
1066 clr DO_TX_UNTHROTTLE
1083 jnb TI, putchar_wait
1088 baud_table: ; baud_high, then baud_low
1090 .byte BAUD_HIGH(110)
1093 .byte BAUD_HIGH(300)
1096 .byte BAUD_HIGH(1200)
1097 .byte BAUD_LOW(1200)
1099 .byte BAUD_HIGH(2400)
1100 .byte BAUD_LOW(2400)
1102 .byte BAUD_HIGH(4800)
1103 .byte BAUD_LOW(4800)
1105 .byte BAUD_HIGH(9600)
1106 .byte BAUD_LOW(9600)
1108 .byte BAUD_HIGH(19200)
1109 .byte BAUD_LOW(19200)
1111 .byte BAUD_HIGH(38400)
1112 .byte BAUD_LOW(38400)
1114 .byte BAUD_HIGH(57600)
1115 .byte BAUD_LOW(57600)
1117 .byte BAUD_HIGH(115200)
1118 .byte BAUD_LOW(115200)
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
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
1133 .word string_langids, string_mfg, string_product, string_serial
1136 string_langids: .byte string_langids_end-string_langids
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:
1147 printf("0x%02x, 0x00, ", ord($c));
1152 string_mfg: .byte string_mfg_end-string_mfg
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
1158 string_product: .byte string_product_end-string_product
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
1164 string_serial: .byte string_serial_end-string_serial
1167 .byte 0x34, 0x00, 0x37, 0x00
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++
1182 ;; rx_ring_in works the same way
1186 .skip 0x100 ; 256 bytes
1188 .skip 0x100 ; 256 bytes