|
Defines |
| #define | HIGH(x) (((x) & 0xff00) / 256) |
| #define | LOW(x) ((x) & 0xff) |
| #define | dpl1 0x84 |
| #define | dph1 0x85 |
| #define | dps 0x86 |
| #define | TX_RUNNING 0 |
| #define | DO_TX_UNTHROTTLE 1 |
| #define | STACK #0x60-1 |
| #define | EXIF 0x91 |
| #define | EIE 0xe8 |
| #define | EP0CS #0x7fb4 |
| #define | EP0STALLbit #0x01 |
| #define | IN0BUF #0x7f00 |
| #define | IN0BC #0x7fb5 |
| #define | OUT0BUF #0x7ec0 |
| #define | OUT0BC #0x7fc5 |
| #define | IN2BUF #0x7e00 |
| #define | IN2BC #0x7fb9 |
| #define | IN2CS #0x7fb8 |
| #define | OUT2BC #0x7fc9 |
| #define | OUT2CS #0x7fc8 |
| #define | OUT2BUF #0x7dc0 |
| #define | IN4BUF #0x7d00 |
| #define | IN4BC #0x7fbd |
| #define | IN4CS #0x7fbc |
| #define | OEB #0x7f9d |
| #define | OUTB #0x7f97 |
| #define | OEC #0x7f9e |
| #define | OUTC #0x7f98 |
| #define | PINSC #0x7f9b |
| #define | PORTBCFG #0x7f94 |
| #define | PORTCCFG #0x7f95 |
| #define | OEA #0x7f9c |
| #define | IN07IRQ #0x7fa9 |
| #define | OUT07IRQ #0x7faa |
| #define | IN07IEN #0x7fac |
| #define | OUT07IEN #0x7fad |
| #define | USBIRQ #0x7fab |
| #define | USBIEN #0x7fae |
| #define | USBBAV #0x7faf |
| #define | USBCS #0x7fd6 |
| #define | SUDPTRH #0x7fd4 |
| #define | SUDPTRL #0x7fd5 |
| #define | SETUPDAT #0x7fe8 |
| #define | BAUD 9600 |
| #define | BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate)) |
| #define | BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate)) |
| #define | BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate)) |
Functions |
| usb flag is | EXIF (0x91).org 0 ljmp start |
| turn on the RS driver | chip (bring the STANDBY pin low) |
| what say we | RENUMERATE (TRM p.62) mov a |
is set HW flow control TODO
ljmp setup_stall is control | pins (RTS, DTR).ljmp control_pins |
will jump to or setup_return_one_byte
is send | break (really"turn break on/off").TODO cjne r3 |
| do break set TxD | high (?)(b1 low) mov dptr |
wValue[0] is threshold value
ljmp setup_done_ack int cjne | Get_Status (device) |
so return two zero bytes This
is reusable IN0BUF clr a movx
a inc dptr movx a mov IN0BC
mov movx a ljmp setup_done_ack | Get_Status (endpoint) |
| string[n] cjne | Get_Descriptor (device) mov dptr |
string[n] cjne SUDPTRH mov
movx a mov SUDPTRL mov movx
a ljmp setup_done_ack | Get_Descriptor (config[n]) cjne r3 |
string[n] cjne SUDPTRH mov
movx a mov SUDPTRL mov movx
a ljmp setup_done_ack | Get_Descriptor (config[0]) mov dptr |
string[n] cjne SUDPTRH mov
movx a mov SUDPTRL mov movx
a ljmp setup_done_ack SUDPTRH
mov movx a mov SUDPTRL mov
movx a ljmp setup_done_ack | Get_Descriptor (string[wValueL]) |
| | if (wValueL >=maxstrings) stall mov a |
driver are too long and are
filled with trailing | garbage (including;;leftover strings).Writing this out by hand is a nuisance |
| there | are (tx_ring_in-tx_ring_out) chars to be written |
| | if (tx_ring_in+1==tx_ring_out) |
Variables |
| stack from x60 to | x7f |
| flag | EUSB |
| flag EIE flag | ES0 |
| usb | interrupt |
interrupt vectors org H ljmp
serial_int byte org H ljmp
USB_Jump_Table | byte |
| filled in by the USB | core |
local variables These are
not initialized | properly |
| wants to be on a page boundary | USB_Jump_Table |
| Setup Data Available byte | ljmp |
End Point Out byte ljmp ISR_Ep2in
byte ljmp ISR_Ep2out byte
org x200 | start |
End Point Out byte ljmp ISR_Ep2in
byte ljmp ISR_Ep2out byte
org x200 | STACK |
| set | stack |
clear local variables clr
a mov | tx_ring_in = = tx_ring_out |
clear local variables clr
a mov a mov | tx_ring_out |
clear local variables clr
a mov a mov a mov | rx_ring_in |
clear local variables clr
a mov a mov a mov a mov | rx_ring_out |
clear local variables clr
a mov a mov a mov a mov a
mov | tx_unthrottle_threshold |
clear local variables clr
a mov a mov a mov a mov a
mov a clr TX_RUNNING clr | DO_TX_UNTHROTTLE |
| clear fifo with fe mov | r1 |
| clear fifo with fe mov mov | a = 4 |
| clear fifo with fe mov mov mov | dptr |
| clear fifo with fe mov mov mov | clear_tx_ring_loop |
clear fifo with fe mov mov
mov a inc dptr djnz clear_tx_ring_loop
mov mov | clear_rx_ring_loop |
| set PORTCCFG[01] to route | TxD0 |
| set up | interrupts |
| set up | autovectoring |
set BKPT mov USBBAV movx dptr
setb | acc |
| arm | OUT2 |
| mov mov | OUTC |
setup the serial port N1 mov
mov | SCON |
| xtal | MHz |
| | RCAP2H |
| | RCAP2L = 65536 - fosc/(32*baud) |
| | __pad68__ |
| say | xFFF3 |
| | __pad69__ |
| say | xFFB2 |
| | __pad70__ |
| mov | r3 |
| acall | dump_stat |
| | hey |
| what say we mov | dps |
| now presence pin is | floating |
now presence pin is simulating
disconnect wait s mov | renum_wait1 |
now presence pin is simulating
disconnect wait s mov | renum_wait2 |
now presence pin is simulating
disconnect wait s mov | renum_wait3 |
now presence pin is simulating
disconnect wait s mov renum_wait3
djnz | r2 |
we are back online the host
device will now re query us | main |
we are back online the host
device will now re query us
EXIF clr acc mov | EXIF |
| clear INT2 first mov | USBIRQ |
| dptr mov | r4 = wValueH |
| main switch on bmRequest | type |
main switch on bmRequest r1
anl cjne standard | request |
a still has bmreq &x60 cjne
Anchor reserves bRequest xa0 | xaf |
a still has bmreq &x60 cjne
Anchor reserves bRequest xa0
we use small | ones |
switch on bRequest bmRequest
will always be x41 or xc1
cjne is set | baud |
switch on bRequest bmRequest
will always be x41 or xc1
cjne is set wValue[0] has
baud rate index lcall | set_baud |
index in carry set if error
jc setup_bmreq_type_not_standard__do_stall
ljmp setup_done_ack | setup_bmreq_type_not_standard__do_stall |
index in carry set if error
jc setup_bmreq_type_not_standard__do_stall
ljmp setup_done_ack is reserved
for set | bits (parity).TODO ljmp setup_stall setup_ctrl_not_01 |
is set HW flow control TODO
ljmp setup_stall | setup_ctrl_not_02 |
| will jump to | setup_done_ack |
| will jump to or setup_return_one_byte | setup_ctrl_not_03 |
will jump to or setup_return_one_byte
is send do break | off |
will jump to or setup_return_one_byte
is send do break PORTCCFG
movx dptr orl movx a ljmp
setup_done_ack | setup_ctrl_do_break_on |
| do break | on |
do break set TxD OUTC movx
dptr anl movx a mov PORTCCFG
movx dptr anl movx a ljmp
setup_done_ack | setup_ctrl_not_04 |
do break set TxD OUTC movx
dptr anl movx a mov PORTCCFG
movx dptr anl movx a ljmp
setup_done_ack is set desired
interrupt bitmap TODO ljmp
setup_stall | setup_ctrl_not_05 |
do break set TxD OUTC movx
dptr anl movx a mov PORTCCFG
movx dptr anl movx a ljmp
setup_done_ack is set desired
interrupt bitmap TODO ljmp
setup_stall is query room
cjne | wValue [0] |
| out | in |
| in out ljmp setup_return_one_byte | setup_ctrl_06_not_01 |
wValue[0] is threshold value
ljmp setup_done_ack | setup_ctrl_not_07 |
wValue[0] is threshold value
ljmp setup_done_ack | ep |
are we self powered no can
we do remote wakeup | no |
so return two zero bytes This
is reusable | setup_return_two_zero_bytes |
so return two zero bytes This
is reusable IN0BUF clr a movx
a inc dptr movx a mov IN0BC
mov movx a ljmp setup_done_ack | setup_Get_Status_not_device |
must get stall bit for return
two | bytes |
must get stall bit for return
two bit in | lsb |
| for | now |
| for | Get_Status (interface) |
| | __pad71__ |
| remote wakeup cjne | Clear_Feature (stall).should clear a stall bit.TODO ljmp setup_stall setup_Clear_Feature_not_stall |
| | __pad72__ |
| remote wakeup cjne | Set_Feature (stall).Should set a stall bit.TODO ljmp setup_stall setup_Set_Feature_not_stall |
| | __pad73__ |
| | config [n] |
string[n] cjne SUDPTRH mov
movx a mov SUDPTRL mov movx
a ljmp setup_done_ack | setup_Get_Descriptor_not_device |
string[n] cjne SUDPTRH mov
movx a mov SUDPTRL mov movx
a ljmp setup_done_ack SUDPTRH
mov movx a mov SUDPTRL mov
movx a ljmp setup_done_ack | setup_Get_Descriptor_not_config |
| add dpl mov | dpl |
| add dpl mov a mov addc dph mov | dph = desc_strings[a]. big endian! (handy) |
it looks like my adapter uses
a revision of the EZUSB | that |
| contains rev D errata | number |
contains rev D errata as hinted
in the EzUSB | example |
code I cannot find an actual
errata description on the | Cypress |
| web | site |
web but from the example code
it looks like this bug | causes |
the length of string descriptors
to be read | incorrectly |
the length of string descriptors
to be read | possibly |
sending back more characters
than the descriptor has The | workaround |
is to manually send out all
of the data The consequence
of | not |
driver are too long and are
filled with trailing so | for |
| done ljmp setup_done_ack | setup_Get_Descriptor_not_string |
| this is reusable mov | setup_return_one_byte |
this is reusable mov IN0BUF
movx a mov mov IN0BC movx
a ljmp setup_done_ack | setup_breq_not_08 |
| since we only have one | interface |
| since we only have one ignore | wIndexL |
since we only have one ignore
return a mov ljmp setup_return_one_byte | setup_breq_not_0a |
since we only have one ignore
return a mov ljmp setup_return_one_byte | b |
now clear HSNAK mov EP0CS
mov movx a sjmp setup_done | setup_stall |
| unhandled | STALL |
| | EP0CS |
EP0CS movx dptr orl EP0STALLbit
movx a sjmp setup_done | setup_done |
| verify | a< 10 mov a, r3 jb ACC.7, set_baud__badbaud clr c subb a,#10 jnc set_baud__badbaud mov a, r3 rl a;a=index *2 add a,#LOW(baud_table) mov dpl, a mov a,#HIGH(baud_table) addc a,#0 mov dph, a;;TODO:shut down xmit/receive;;TODO:wait for current xmit char to leave;;TODO:shut down timer to avoid partial-char glitch movx a,@dptr;BAUD_HIGH mov RCAP2H, a mov TH2, a inc dptr movx a,@dptr;BAUD_LOW mov RCAP2L, a mov TL2, a;;TODO:restart xmit/receive;;TODO:reenable interrupts, resume tx if pending clr c;c=0:success retset_baud__badbaud:setb c;c=1:failure ret;;;==================================================control_pins:cjne r1,#0x41, control_pins_incontrol_pins_out:;TODO BKPT is DTR mov a, r3;wValue[0] holds new bits:b7 is new RTS xrl a,#0xff;1 means active, 0V,+12V?anl a,#0x80 mov r3, a mov dptr, OUTC movx a,@dptr;only change bit 7anl a,#0x7F;~0x84 orl a, r3 movx @dptr, a;other pins are inputs, bits ignored ljmp setup_done_ackcontrol_pins_in:mov dptr, PINSC movx a,@dptr xrl a,#0xff ljmp setup_return_one_byte;;;========================================ISR_Ep2in:push dps push dpl push dph push dpl1 push dph1 push acc mov a, EXIF clr acc.4 mov EXIF, a;clear INT2 first mov dptr, IN07IRQ;clear USB int mov a,#04h movx @dptr, a mov a,#0x20;Turn off the green LEDmov dptr, OEAmovx @dptr, a;;do stuff lcall start_in mov a,#0x20;Turn off the green LEDmov dptr, OEAmovx @dptr, apop acc pop dph1 pop dpl1 pop dph pop dpl pop dps retiISR_Ep2out:push dps push dpl push dph push dpl1 push dph1 push accmov a,#0x10;Turn the green LEDmov dptr, OEAmovx @dptr, a mov a, EXIF clr acc.4 mov EXIF, a;clear INT2 first mov dptr, OUT07IRQ;clear USB int mov a,#04h movx @dptr, a;;do stuff;;copy data into buffer.for now, assume we will have enough space mov dptr, OUT2BC;get byte count movx a,@dptr mov r1, a clr a mov dps, a mov dptr, OUT2BUF;load DPTR0 with source mov dph1,#HIGH(tx_ring);load DPTR1 with target mov dpl1, tx_ring_inOUT_loop:movx a,@dptr;read inc dps;switch to DPTR1:target inc dpl1;target=tx_ring_in+1 movx @dptr, a;store mov a, dpl1 cjne a, tx_ring_out, OUT_no_overflow sjmp OUT_overflowOUT_no_overflow:inc tx_ring_in;tx_ring_in++inc dps;switch to DPTR0:source inc dptr djnz r1, OUT_loop sjmp OUT_doneOUT_overflow:;;signal overflow;;fall throughOUT_done:;;ack mov dptr, OUT2BC movx @dptr, a;;start tx acall maybe_start_tx;acall dump_statmov a,#0x20;Turn off the green LEDmov dptr, OEAmovx @dptr, apop acc pop dph1 pop dpl1 pop dph pop dpl pop dps retidump_stat:;;fill in EP4in with a debugging message:;;tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out;;tx_active;;tx_ring[0..15];;0xfc;;rx_ring[0..15] clr a mov dps, amov dptr, IN4CS movx a,@dptr jb acc.1, dump_stat__done;busy:cannot dump, old one still pending mov dptr, IN4BUFmov a, tx_ring_in movx @dptr, a inc dptr mov a, tx_ring_out movx @dptr, a inc dptr mov a, rx_ring_in movx @dptr, a inc dptr mov a, rx_ring_out movx @dptr, a inc dptrclr a jnb TX_RUNNING, dump_stat__no_tx_running inc adump_stat__no_tx_running:movx @dptr, a inc dptr;;tx_ring[0..15] inc dps mov dptr,#tx_ring;DPTR1:source mov r1,#16dump_stat__tx_ring_loop:movx a,@dptr inc dptr inc dps movx @dptr, a inc dptr inc dps djnz r1, dump_stat__tx_ring_loop inc dpsmov a,#0xfc movx @dptr, a inc dptr;;rx_ring[0..15] inc dps mov dptr,#rx_ring;DPTR1:source mov r1,#16dump_stat__rx_ring_loop:movx a,@dptr inc dptr inc dps movx @dptr, a inc dptr inc dps djnz r1, dump_stat__rx_ring_loop;;now send it clr a mov dps, a mov dptr, IN4BC mov a,#38 movx @dptr, adump_stat__done:ret;;;============================================================maybe_start_tx:;;make sure the tx process is running.jb TX_RUNNING, start_tx_donestart_tx:;;is there work to be done?mov a, tx_ring_in cjne a, tx_ring_out, start_tx__work ret;no workstart_tx__work:;;tx was not running.send the first character, setup the TI int inc tx_ring_out;[++tx_ring_out] mov dph,#HIGH(tx_ring) mov dpl, tx_ring_out movx a,@dptr mov sbuf, a setb TX_RUNNINGstart_tx_done:;;can we unthrottle the host tx process?;;step 1:do we care?mov a,#0 cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx;;nopestart_tx_really_done:retstart_tx__maybe_unthrottle_tx:;;step 2:is there now room?mov a, tx_ring_out setb c subb a, tx_ring_in;;a is now write_room.If thresh > = a |
verify we can unthrottle clr
c subb tx_unthrottle_threshold
jc | start_tx_really_done |
| | nope |
| | yes |
| prod | rx |
prod which will actually send
the message when in2 becomes
free ljmp start_in | serial_int |
prod which will actually send
the message when in2 becomes
free ljmp start_in | serial_int__not_tx |
tx finished send another character
if we have one clr | TI |
clear int clr TX_RUNNING lcall
start_tx serial_int__not_rx
lcall get_rx_char clr | RI |
| clear int | serial_int__not_rx |
return pop acc pop dph1 pop
dpl1 pop dph pop dpl pop dps
reti | get_rx_char |
| | target |
check for overflow before
incrementing rx_ring_in mov
dpl cjne | get_rx_char__no_overflow |
kick off USB INpipe acall
start_in ret | start_in |
check if the inpipe is already
running mov mov OEA movx a
mov IN2CS movx dptr jb | start_in__done |
| int will handle it jb | start_in__do_tx_unthrottle |
see if there is any work to
do a serial interrupt might | occur |
during this sequence mov rx_ring_in
cjne start_in__have_work | ret |
| nope | start_in__have_work |
now copy as much data as possible
into the pipe bytes max clr
a mov a mov inc dps mov | IN2BUF |
| loop until we run out of | data |
loop until we run out of or
we have copied bytes mov | start_in__loop |
loop until we run out of or
we have copied bytes mov rx_ring_in
cjne start_in__still_copying
sjmp start_in__kick | start_in__still_copying |
write into IN buffer inc dptr
inc dps inc r1 cjne | start_in__kick |
either we ran out of or we
copied bytes r1 has byte | count |
| special | sequence |
special a mov IN2BUF mov movx
a inc dptr mov movx a mov
IN2BC movx a ret | putchar |
special a mov IN2BUF mov movx
a inc dptr mov movx a mov
IN2BC movx a ret a | putchar_wait |
special a mov IN2BUF mov movx
a inc dptr mov movx a mov
IN2BC movx a ret a putchar_wait
clr TI ret | baud_table |
| | baud_high |
| then | baud_low |
| baud[0] | __pad74__ |
| baud[1] | __pad75__ |
| baud[2] | __pad76__ |
| baud[3] | __pad77__ |
| baud[4] | __pad78__ |
| baud[5] | __pad79__ |
| baud[6] | __pad80__ |
| baud[7] | __pad81__ |
| baud[8] | __pad82__ |
| baud[9] | __pad83__ |
| baud[9] | x01 |
| baud[9] | x00 |
| baud[9] | xff |
| baud[9] x40 byte | xcd |
| baud[9] x40 byte | x06 |
| baud[9] x40 byte | x04 |
| baud[9] x40 byte | x89 |
| baud[9] x40 byte | xab |
| The real device | id |
The real device which must
match the host | driver |
| xcd x06 x04 x01 which is | x06cd |
| xcd x06 x04 x01 which is x0104 | desc_config1 |
| xcd x06 x04 x01 which is x0104 | x02 |
| xcd x06 x04 x01 which is x0104 | x20 |
| xcd x06 x04 x01 which is x0104 | x80 |
xcd x06 x04 x01 which is x0104
x32 byte | x09 |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte | x07 |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte | x05 |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte | x82 |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte | x03 |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte | x40 |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte x01 byte
x00 | desc_strings |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte x01 byte
x00 | string_mfg |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte x01 byte
x00 | string_product |
xcd x06 x04 x01 which is x0104
x32 byte x00 byte x01 byte
x00 string_serial | desc_strings_end |
| sigh These strings are | Unicode |
sigh These strings are meaning
UTF16 bytes each | Now |
*that *is a pain in the ass
to encode And they are little | endian |
too Use this perl snippet
to get the | bytecodes |
| byte ACME usb widgets byte | x41 |
| byte ACME usb widgets byte | x43 |
| byte ACME usb widgets byte | x4d |
| byte ACME usb widgets byte | x45 |
| byte ACME usb widgets byte | x75 |
| byte ACME usb widgets byte | x73 |
| byte ACME usb widgets byte | x62 |
| byte ACME usb widgets byte | x77 |
| byte ACME usb widgets byte | x69 |
| byte ACME usb widgets byte | x64 |
| byte ACME usb widgets byte | x67 |
| byte ACME usb widgets byte | x65 |
| byte ACME usb widgets byte | x74 |
| byte ACME usb widgets byte x00 | string_mfg_end |
byte ACME USB serial widget
byte | x55 |
byte ACME USB serial widget
byte | x53 |
byte ACME USB serial widget
byte | x42 |
byte ACME USB serial widget
byte | x72 |
byte ACME USB serial widget
byte | x61 |
byte ACME USB serial widget
byte | x6c |
byte ACME USB serial widget
byte x00 | string_product_end |
| byte byte | x34 |
| byte byte | x37 |
| byte byte x00 | string_serial_end |
| ring buffer | memory |
tx_ring_in is where the next
input byte will | go |
| has been | sent |
| if theres no work to | do |
| dont let _in lap | _out |
| | overflow |
| read | send [tx_ring_out+1] |
rx_ring_in works the same
way org x1000 | tx_ring |
| bytes | rx_ring |