' Copyright Brad Campbell, 2007 - All rights reserved ' ' Thanks to Dick Dick Streefland and Igor Cesko (Not quite sure the unicode translates) ' for inspiration ' http://www.xs4all.nl/~dicks/avr/usbtiny/index.html ' http://cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm ' ' Thanks to Michael Hetherington for the lyrics ' http://www.sxlist.com/techref/scenix/lib/io/dev/keys/usbdemo-mh.htm ' The MAC COG relies on the CRC table defined in here, don't replace it without altering ' the MAC COG code! CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 ' *Must* be a clean all ones bitmask VAR long ring ' Cog paramaters contiguous block of longs ' each endpoint in/out has 4 longs ' IN endpoints 1st ' byte counter ' bytes [0-3] ' bytes [4-7] ' spare ' OUT endpoints ' byte counter ' bytes [0-3] ' bytes [4-7] ' bytes [8-9] justified to LSB long endpoints[32] long packetcount long packetbad OBJ ll : "USB_lowlevel_005" PUB start : OK | i ring := 0 Pcrcmask := @crc16table Dtable := @dd_table Word[Dtable][0] := 0 Word[Dtable][1] := @dd_device Word[Dtable][2] := @dd_config Word[Dtable][3] := @dd_status Word[Dtable][4] := 0 Word[Dtable][5] := @dd_langid Word[Dtable][6] := @dd_vendor Word[Dtable][7] := @dd_product Word[Dtable][8] := 0 Word[Dtable][9] := @dd_report Word[Dtable][10] := @dd_hello long[@aring] := 0 i := @endpoints epb := i longfill(i, 0, 32) OK := ll.start(@crc16table, epb) OK := cognew(@USB_APP, 0) DAT USB_APP org wrlong zero, epb ' Spark up MAC thread now we are running usboot mov lflags, #0 mov out, #0 :loop mov pointer, epb rdlong da, pointer wz IF_NZ call #process_0i add pointer, #16 rdlong da, pointer wz IF_NZ call #process_1i add pointer, #16 rdlong da, pointer wz IF_NZ call #process_2i add pointer, #16 rdlong da, pointer wz IF_NZ call #process_3i add pointer, #16 test out, #1 wz IF_Z jmp #:loop1 rdlong da, pointer wz IF_Z call #process_0o :loop1 add pointer, #16 test out, #2 wz IF_Z jmp #:loop2 rdlong da, pointer wz IF_Z call #process_1o :loop2 add pointer, #16 test out, #4 wz IF_Z jmp #:loop3 rdlong da, pointer wz IF_Z call #process_2o :loop3 add pointer, #16 test out, #8 wz IF_Z jmp #:loop4 rdlong da, pointer wz IF_Z call #process_3o :loop4 jmp #:loop reset mov bytecnt, #0 mov flags, #0 mov epoint, #0 call #rdbuf add epoint, #1 call #rdbuf add epoint, #1 call #rdbuf add epoint, #1 call #rdbuf jmp #usboot process_0i test da, flag_reset wz IF_NZ jmp #reset ' Lower level has requested a system reset mov epoint, #0 call #rdbuf test buf0, flag_setup wz ' Are we a setup packet? IF_Z jmp #:end mov dc, buf1 shr dc, #8 and dc, #$FF ' Request Number mov flags, #0 mov bytecnt, buf0 and bytecnt, #$FF cmp bytecnt, #8 wz ' Number of bytes IF_NZ jmp #:end ' All setup packets are 8 bytes test buf0, flag_class wz ' Are we a class packet? IF_NZ jmp #:class cmp dc, #6 wz ' GET_DESCRIPTOR IF_NZ jmp #:setup3 mov dd, buf1 shr dd, #24 and dd, #$FF mov da, #0 cmp dd, #1 wz ' Device Descriptor IF_Z mov da, #1 cmp dd, #2 wz ' Config Descriptor IF_Z mov da, #2 cmp dd, #3 wz ' String Descriptor (see below) IF_Z jmp #:ss_string_d cmp dd, #$22 wz IF_Z mov da, #9 ' Report descriptor tjz da, #:send_empty ' Have we anything to send? jmp #:queue ' These string ID's are tied to the ID's specified in the Config Descriptor ' Make it easy and use 1/2/3. 0 is Mandatory if you have 1, 2 or 3. :ss_string_d mov dd, buf1 shr dd, #16 and dd, #$FF cmp dd, #0 wz IF_Z mov da, #5 ' Language ID cmp dd, #1 wz IF_Z mov da, #6 ' Vendor String cmp dd, #2 wz IF_Z mov da, #7 ' Device Name String cmp dd, #3 wz IF_Z mov da, #8 ' Serial Number String tjz da, #:send_empty ' Have we anything to send? :queue mov ep0bl, buf2 shr ep0bl, #16 and ep0bl, #$1FF ' Max length call #queue_string test db, allones wz IF_Z jmp #:send_empty ' No string to queue mov ep0len, db mov ep0buf, da or lflags, flag_debug or out, #1 ' Endpoint 0 jmp #:end :setup3 cmp dc, #5 wz ' Address change packet IF_NZ jmp #:setup4 or flags, flag_address ' Address change is handled in the low level driver jmp #:send_empty :setup4 cmp dc, #0 wz ' Status request (need this if we are not picked up by a driver - lsusb causes a reboot) IF_NZ jmp #:setup5 mov da, #3 jmp #:queue :setup5 cmp dc, #9 wz ' Set Config IF_NZ jmp #:send_empty :send_empty or flags, flag_empty1 ' STATUS requests requires a DATA1 call #wrbuf jmp #:end :class cmp dc, #9 wz ' Set report IF_Z call #dreamin test b_1, #$80 wz ' Host direction bit IF_NZ jmp #:end ' No need for empty packet jmp #:send_empty :end mov da, epb wrlong zero, epb process_0i_ret ret ' da is table index, dc is maximum length ' returns length in db and address in da ' if db is 0 there is no string queue_string shl da, #1 add da, Dtable rdword db, da wz IF_Z jmp #queue_string_ret ' Descriptor is empty mov da, db rdbyte db, da ' Queue up the descriptor and send it off add da, #1 queue_string_ret ret dreamin or out, #2 ' Enable endpoint 1 mov da, #10 ' Text of song call #queue_string mov ep1buf, da mov ep1len, db mov ep1toggle, #0 dreamin_ret ret ' This queues up the endpoint for action process_1i mov epoint, #1 call #rdbuf process_1i_ret ret process_2i mov epoint, #2 call #rdbuf process_2i_ret ret process_3i mov epoint, #3 call #rdbuf process_3i_ret ret process_0o mov epoint, #0 mov epbuf, ep0buf mov eplen, ep0len mov epbl, ep0bl call #ep_string_out mov ep0bl, epbl mov ep0buf, epbuf mov ep0len, eplen process_0o_ret ret process_2o mov epoint, #2 mov flags, #0 mov bytecnt, #0 call #wrbuf process_2o_ret ret process_3o mov epoint, #3 mov flags, #0 mov bytecnt, #0 call #wrbuf process_3o_ret ret process_1o test ep1len, allones wz IF_Z andn out, #2 ' Disable endpoint 1 IF_Z jmp #process_1o_ret call #bufstart mov data, #0 call #pushbuf call #pushbuf xor ep1toggle, #1 wz IF_Z jmp #:keyup rdbyte data, ep1buf call #pushbuf ' Key down mov data, #0 jmp #:next :keyup add ep1buf, #1 sub ep1len, #1 call #pushbuf ' Key up :next call #pushbuf call #pushbuf call #pushbuf call #pushbuf call #pushbuf call #pushcrc mov bytecnt, #$0A call #packem mov epoint, #1 call #wrbuf process_1o_ret ret rdbuf mov da, epoint shl da, #4 add da, epb rdlong buf0, da add da, #4 rdlong buf1, da add da, #4 rdlong buf2, da sub da, #8 wrlong zero, da ' Clear the buffer, we have it now rdbuf_ret ret wrbuf mov da, epoint shl da, #4 add da, epb add da, #68 ' 64 + skip 1st long wrlong buf0, da add da, #4 mov db, flags ' Interleaved wrlong buf1, da add da, #4 or db, bytecnt ' Interleaved wrlong buf2, da mov flags, #0 sub da, #12 wrlong db, da ' Byte Counter / Flags wrbuf_ret ret packem ' 25 Instructions mov buf0, b_1 mov da, b_2 shl da, #8 or buf0, da mov da, b_3 shl da, #16 or buf0, da mov da, b_4 shl da, #24 or buf0, da mov buf1, b_5 mov da, b_6 shl da, #8 or buf1, da mov da, b_7 shl da, #16 or buf1, da mov da, b_8 shl da, #24 or buf1, da mov buf2, b_9 mov da, b_10 shl da, #8 or buf2, da packem_ret ret unpack_buffer mov da, buf1 mov b_1, da shr da, #8 mov b_2, da shr da, #8 mov b_3, da shr da, #8 mov b_4, da mov da, buf2 mov b_5, da shr da, #8 mov b_6, da shr da, #8 mov b_7, da shr da, #8 mov b_8, da unpack_buffer_ret ret pushbuf movd :pindr, bptr add bptr, #1 :pindr mov 0-0, data call #crc16byte add bytecnt, #1 pushbuf_ret ret ' 13 Instructions pushcrc movd :pcrc1, bptr add bptr, #1 movd :pcrc2, bptr xor crc, allones mov data, crc and data, #$FF :pcrc1 mov 0-0, data mov data, crc shr data, #8 and data, #$FF :pcrc2 mov 0-0, data add bytecnt, #2 pushcrc_ret ret bufstart mov bptr, #b_1 mov crc, crcmask mov bytecnt, #0 bufstart_ret ret ' 30 Instructions not including subroutines ep_string_out test eplen, allones wz IF_Z jmp #:send_empty call #bufstart mov da, #8 :send_loop rdbyte data, epbuf add epbuf, #1 call #pushbuf sub da, #1 sub eplen, #1 sub epbl, #1 tjz da, #:send_next tjz epbl, #:send_next tjnz eplen, #:send_loop :send_next call #pushcrc call #packem jmp #:next :send_empty mov bytecnt, #0 mov flags, flag_empty :next call #wrbuf mov data, #$20 add data, epoint test epbl, allones wz ' If we are at the end of the buffer or request length we don't IF_Z jmp #:here ' send an empty packet cmp bytecnt, #$0A wz IF_Z jmp #ep_string_out_ret :here mov da, #1 shl da, epoint andn out, da andn lflags, flag_debug ep_string_out_ret ret crcmask long $0000FFFF crc16byte ' data has the byte to add to the crc. it's not touched mov t, data xor t, crc and t, #$FF shl t, #1 add t, Pcrcmask rdword t1, t shr crc, #8 xor crc, t1 crc16byte_ret ret allones long $FFFFFFFF zero long 0 aring long 0 Pcrcmask long 0 epb long 0 out long 0 ep0len long 0 ep0buf long 0 ep0bl long 0 ep1len long 0 ep1buf long 0 ep1bl long 0 ep1toggle long 0 ep2len long 0 ep2buf long 0 ep2bl long 0 ep3len long 0 ep3buf long 0 ep3bl long 0 eplen long 0 epbuf long 0 epbl long 0 bptr long 0 buf0 long 0 buf1 long 0 buf2 long 0 b_1 long 0 b_2 long 0 b_3 long 0 b_4 long 0 b_5 long 0 b_6 long 0 b_7 long 0 b_8 long 0 b_9 long 0 b_10 long 0 rxh long 0 rxt long 0 txh long 0 txt long 0 rxb long 0 txb long 0 flag_reset long 1 << 22 flag_rtoggle long 1 << 23 flag_empty1 long 1 << 24 ' Send an empty DATA1 packet flag_debug long 1 << 25 ' Debugging used in the app level flag_class long 1 << 26 ' We recieved a CLASS request flag_address long 1 << 27 ' Inbound setup packet is an address request flag_empty long 1 << 28 ' Send next logical DATAX token in an empty packet flag_setup long 1 << 29 ' Inbound packet is a setup request flag_serr long 1 << 30 ' Bit stuffing error detected flag_crc long 1 << 31 ' CRC error detected Dtable long 0 flags res 1 lflags res 1 pointer res 1 epoint res 1 ' inbound endpoint t res 1 t1 res 1 data res 1 da res 1 db res 1 dc res 1 dd res 1 crc res 1 bytecnt res 1 fit $1F0 crc16table word $0000, $c0c1, $c181, $0140, $c301, $03c0, $0280, $c241, $c601, $06c0, $0780 word $c741, $0500, $c5c1, $c481, $0440, $cc01, $0cc0, $0d80, $cd41, $0f00, $cfc1 word $ce81, $0e40, $0a00, $cac1, $cb81, $0b40, $c901, $09c0, $0880, $c841, $d801 word $18c0, $1980, $d941, $1b00, $dbc1, $da81, $1a40, $1e00, $dec1, $df81, $1f40 word $dd01, $1dc0, $1c80, $dc41, $1400, $d4c1, $d581, $1540, $d701, $17c0, $1680 word $d641, $d201, $12c0, $1380, $d341, $1100, $d1c1, $d081, $1040, $f001, $30c0 word $3180, $f141, $3300, $f3c1, $f281, $3240, $3600, $f6c1, $f781, $3740, $f501 word $35c0, $3480, $f441, $3c00, $fcc1, $fd81, $3d40, $ff01, $3fc0, $3e80, $fe41 word $fa01, $3ac0, $3b80, $fb41, $3900, $f9c1, $f881, $3840, $2800, $e8c1, $e981 word $2940, $eb01, $2bc0, $2a80, $ea41, $ee01, $2ec0, $2f80, $ef41, $2d00, $edc1 word $ec81, $2c40, $e401, $24c0, $2580, $e541, $2700, $e7c1, $e681, $2640, $2200 word $e2c1, $e381, $2340, $e101, $21c0, $2080, $e041, $a001, $60c0, $6180, $a141 word $6300, $a3c1, $a281, $6240, $6600, $a6c1, $a781, $6740, $a501, $65c0, $6480 word $a441, $6c00, $acc1, $ad81, $6d40, $af01, $6fc0, $6e80, $ae41, $aa01, $6ac0 word $6b80, $ab41, $6900, $a9c1, $a881, $6840, $7800, $b8c1, $b981, $7940, $bb01 word $7bc0, $7a80, $ba41, $be01, $7ec0, $7f80, $bf41, $7d00, $bdc1, $bc81, $7c40 word $b401, $74c0, $7580, $b541, $7700, $b7c1, $b681, $7640, $7200, $b2c1, $b381 word $7340, $b101, $71c0, $7080, $b041, $5000, $90c1, $9181, $5140, $9301, $53c0 word $5280, $9241, $9601, $56c0, $5780, $9741, $5500, $95c1, $9481, $5440, $9c01 word $5cc0, $5d80, $9d41, $5f00, $9fc1, $9e81, $5e40, $5a00, $9ac1, $9b81, $5b40 word $9901, $59c0, $5880, $9841, $8801, $48c0, $4980, $8941, $4b00, $8bc1, $8a81 word $4a40, $4e00, $8ec1, $8f81, $4f40, $8d01, $4dc0, $4c80, $8c41, $4400, $84c1 word $8581, $4540, $8701, $47c0, $4680, $8641, $8201, $42c0, $4380, $8341, $4100 word $81c1, $8081, $4040 crc16tableend dd_table word 0 word @dd_device ' 1 word @dd_config ' 2 word @dd_status ' 3 word 0 ' 4 word @dd_langid ' 5 word @dd_vendor ' 6 word @dd_product ' 7 word 0 ' 8 word @dd_report ' 9 word 0 ' 10 word 0 ' 11 word 0 ' 12 word 0 ' 13 dd_device byte @dd_device_end-(@dd_device+1) byte @dd_device_end-(@dd_device+1) byte 01, 00, 01 , 00, 00, 00, 08, $C0, $16, $E1, $06, 01, 00, 01, 02, 00, 01 dd_device_end dd_config byte @dd_endpoint_e-(@dd_config+1) byte @dd_config_end-(@dd_config+1) byte 02 byte @dd_endpoint_e-(@dd_config+1) byte 00, 01, 01, 00, $A0, $32 dd_config_end dd_inter byte @dd_inter_e-@dd_inter byte 04, 00, 00, 01, 03, 01, 01, 00 dd_inter_e dd_class byte @dd_class_e-@dd_class byte $21, 00, 01, 00, 01, $22 byte @dd_report_e-(@dd_report+1), $0 dd_class_e dd_endpoint byte @dd_endpoint_e-@dd_endpoint byte 5, $81, $03, $08, $00, $0A dd_endpoint_e dd_report byte @dd_report_e-(@dd_report+1) byte $5, $1, $9, $6, $A1, $1, $5, $7, $19, $E0, $29, $E7, $15, $0, $25, $0 byte $75, $1, $95, $8, $81, $2, $95, $1, $75, $8, $81, $1, $95, $5, $75, $1 byte $5, $8, $19, $1, $29, $5, $91, $2, $95, $3, $75, $1, $91, $1, $95, $6 byte $75, $8, $15, $0, $25, $65, $5, $7, $19, $0, $29, $65, $81, 0, $c0 dd_report_e dd_hello byte @dd_hello_e-(@dd_hello+1) byte $28, $17,$12,$2C,$10,$1C,$2C,$13,$04,$15,$08,$11,$17,$16,$28,$28 'All the leaves are brown and the sky is gray byte $04,$0F,$0F,$2C,$17,$0B,$08,$2C,$0F,$08,$04,$19,$08,$16,$2C byte $04,$15,$08,$2C,$05,$15,$12,$1A,$11,$2C,$04,$11,$07,$2C byte $17,$0B,$08,$2C,$16,$0E,$1C,$2C,$0C,$16,$2C,$0A,$15,$04,$1C,$28 'I've been for a walk on a winter's day byte $0C,$34,$19,$08,$2C,$05,$08,$08,$11,$2C,$09,$12,$15,$2C,$04,$2C,$1A,$04,$0F,$0E,$2C byte $12,$11,$2C,$04,$2C,$1A,$0C,$11,$17,$08,$15,$34,$16,$2C,$07,$04,$1C,$28 'I'd be safe and warm if I was in L.A. byte $0C,$34,$07,$2C,$05,$08,$2C,$16,$04,$09,$08,$2C,$04,$11,$07,$2C byte $1A,$04,$15,$10,$2C,$0C,$09,$2C,$0C,$2C,$1A,$04,$16,$2C byte $0C,$11,$2C,$0F,$37,$04,$37,$28 'california dreamin' on such a winter's day byte $06,$04,$0F,$0C,$09,$12,$15,$11,$0C,$04,$2C,$07,$15,$08,$04,$10,$0C,$11,$34,$2C byte $12,$11,$2C,$16,$18,$06,$0B,$2C,$04,$2C,$1A,$0C,$11,$17,$08,$15,$34,$16,$2C byte $07,$04,$1C,$28 dd_hello_e dd_vendor byte @dd_vendor_e-(@dd_vendor+1) byte @dd_vendor_e-(@dd_vendor+1) byte 3 byte "B",0,"r",0,"a",0,"d",0,"s",0,"-",0,"P",0,"r",0,"o",0,"p",0,"e",0,"l",0,"l",0,"e",0,"r",0 dd_vendor_e dd_product byte @dd_product_e-(@dd_product+1) byte @dd_product_e-(@dd_product+1) byte 3 byte "H",0,"I",0,"D",0,"-",0,"T",0,"e",0,"s",0,"t",0,"-",0,"0",0,"1",0 dd_product_e dd_langid byte 4 byte 4, 3, $04, $09 dd_langid_e dd_status byte 2 byte 0, 0 dd_status_e