From: Frank Mori Hess Date: Sun, 1 Aug 2004 22:24:01 +0000 (+0000) Subject: updated usbdux firmware from Bernd Porr: X-Git-Tag: r0_7_22~17 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=cdb4e7d266dfb2f8f02a19bb75a13cb3141744db;p=comedilib.git updated usbdux firmware from Bernd Porr: Firmware now only measures as many channels as are in the channel list. If there's only one channel in the list we can manage to measure within one microframe. This makes it possible to measure with 8kHz max. Added a counter. The counter is called every 2ms from an FX2 timer inerrupt. Low priority so that measurements are not disturbed. The bulk transfers are now two times faster (insn). Reading an A/D value or an DIO takes now only 2ms or less. The trick is that the in endpoint is already filled when the request is issued. Fixed a bug in the FIFO resets. Second counter did not count properly. That's fixed. Also found two missing syncdelays. --- diff --git a/etc/hotplug/usb/usbdux/usbdux_firmware.asm b/etc/hotplug/usb/usbdux/usbdux_firmware.asm index e9aa88b..3513b1c 100644 --- a/etc/hotplug/usb/usbdux/usbdux_firmware.asm +++ b/etc/hotplug/usb/usbdux/usbdux_firmware.asm @@ -1,5 +1,6 @@ ; usbdux_firmware.asm -; Copyright (C) 2004 Bernd Porr, Bernd.Porr@cn.stir.ac.uk +; Copyright (C) 2004 Bernd Porr, Bernd.Porr@f2s.com +; For usbdux.c 1.00pre2 ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by @@ -19,8 +20,8 @@ ; Firmware: usbdux_firmware.asm for usbdux.c ; Description: University of Stirling USB DAQ & INCITE Technology Limited ; Devices: [ITL] USB-DUX (usbdux.o) -; Author: Bernd Porr -; Updated: 25 Jan 2004 +; Author: Bernd Porr +; Updated: 23 Jul 2004 ; Status: testing ; ;;; @@ -31,11 +32,21 @@ .equ CHANNELLIST,80h ; channellist in indirect memory - .equ DIOFLAG,90h ; flag if next IN transf is DIO - + .equ CMD_FLAG,90h ; flag if next IN transf is DIO + .equ SGLCHANNEL,91h ; channel for INSN + + .equ DIOSTAT0,98h ; last status of the digital port + .equ DIOSTAT1,99h ; same for the second counter + + .equ CTR0,0A0H ; counter 0 + .equ CTR1,0A2H ; counter 1 + .org 0000h ; after reset the processor starts here ljmp main ; jump to the main loop + .org 000bh ; timer 0 irq + ljmp timer0_isr + .org 0043h ; the IRQ2-vector ljmp jmptbl ; irq service-routine @@ -164,18 +175,25 @@ ep2_isr: main: mov DPTR,#CPUCS ; CPU control register mov a,#00010000b ; 48Mhz - movx @DPTR,a ; do it - lcall syncdelay + lcall syncdelaywr + mov dptr,#REVCTL + mov a,#00000011b ; allows skip + lcall syncdelaywr + + mov IP,#0 ; all std 8051 int have low priority + mov EIP,#0FFH ; all FX2 interrupts have high priority + mov dptr,#INTSETUP ; IRQ setup register mov a,#08h ; enable autovector - movx @DPTR,a ; do it - lcall syncdelay + lcall syncdelaywr lcall initAD ; init the ports to the converters lcall initeps ; init the isochronous data-transfer + lcall init_timer + mloop2: nop nop nop @@ -184,7 +202,7 @@ mloop2: nop nop nop - sjmp mloop2 ; do nothing. The rest is done by the IRQs + sjmp mloop2 ; loop for ever ;;; initialise the ports for the AD-converter @@ -194,6 +212,16 @@ initAD: ret +;;; init the timer for the soft counters +init_timer: + ;; init the timer for 2ms sampling rate + mov CKCON,#00000001b; CLKOUT/12 for timer + mov TL0,#010H ; 16 + mov TH0,#0H ; 256 + mov IE,#82H ; switch on timer interrupt (80H for all IRQs) + mov TMOD,#00000000b ; 13 bit counters + setb TCON.4 ; enable timer 0 + ret ;;; from here it's only IRQ handling... @@ -264,82 +292,27 @@ zerob2: mov a,r5 ; get r5 in order to shift the mask -;;; aquires data from all 8 channels and stores it in the EP6 buffer -convlo: ;; - mov AUTOPTRH1,#0F8H ; EP6 +;;; aquires data from A/D channels and stores them in the EP6 buffer +conv_ad: + mov AUTOPTRH1,#0F8H ; auto pointer on EP6 mov AUTOPTRL1,#00H mov AUTOPTRSETUP,#7 mov r0,#CHANNELLIST ; points to the channellist - mov a,@r0 ;Ch0 - lcall readAD - mov a,R3 ; - mov DPTR,#XAUTODAT1 - movx @DPTR,A - mov a,R4 ; - movx @DPTR,A - - inc r0 ; next channel - mov a,@r0 ;Ch1 - lcall readAD - mov a,R3 ; - mov DPTR,#XAUTODAT1 - movx @DPTR,A - mov a,R4 ; - movx @DPTR,A - - inc r0 - mov a,@r0 ;Ch2 - lcall readAD - mov a,R3 ; - mov DPTR,#XAUTODAT1 - movx @DPTR,A - mov a,R4 ; - movx @DPTR,A - inc r0 - mov a,@r0 ;Ch3 - lcall readAD - mov a,R3 ; - mov DPTR,#XAUTODAT1 - movx @DPTR,A - mov a,R4 ; - movx @DPTR,A + mov a,@r0 ; number of channels + mov r1,a ; counter + mov DPTR,#XAUTODAT1 ; auto pointer +convloop: inc r0 - mov a,@r0 ;Ch4 + mov a,@r0 ; Channel lcall readAD mov a,R3 ; - mov DPTR,#XAUTODAT1 movx @DPTR,A mov a,R4 ; movx @DPTR,A + djnz r1,convloop - inc r0 - mov a,@r0 ;Ch5 - lcall readAD - mov a,R3 ; - mov DPTR,#XAUTODAT1 - movx @DPTR,A - mov a,R4 ; - movx @DPTR,A - - inc r0 - mov a,@r0 ;Ch6 - lcall readAD - mov a,R3 ; - mov DPTR,#XAUTODAT1 - movx @DPTR,A - mov a,R4 ; - movx @DPTR,A - - inc r0 - mov a,@r0 ;Ch7 - lcall readAD - mov a,R3 ; - mov DPTR,#XAUTODAT1 - movx @DPTR,A - mov a,R4 ; - movx @DPTR,A ret @@ -349,9 +322,17 @@ convlo: ;; ;;; It is assumed that the USB interface is in alternate setting 3 initeps: mov dptr,#FIFORESET - mov a,#0fh + mov a,#80H movx @dptr,a ; reset all fifos - mov a,#00h + mov a,#2 + movx @dptr,a ; + mov a,#4 + movx @dptr,a ; + mov a,#6 + movx @dptr,a ; + mov a,#8 + movx @dptr,a ; + mov a,#0 movx @dptr,a ; normal operat mov DPTR,#EP2CFG @@ -363,7 +344,7 @@ initeps: movx @dptr,a mov dptr,#EP2BCL ; "arm" it - mov a,#80h + mov a,#00h movx @DPTR,a ; can receive data lcall syncdelay ; wait to sync movx @DPTR,a ; can receive data @@ -380,7 +361,7 @@ initeps: movx @dptr,a mov dptr,#EP4BCL ; "arm" it - mov a,#80h + mov a,#00h movx @DPTR,a ; can receive data lcall syncdelay ; wait until we can write again movx @dptr,a ; make shure its really empty @@ -410,11 +391,103 @@ initeps: mov EIE,#00000001b ; enable INT2 in the 8051's SFR mov IE,#80h ; IE, enable all interrupts - lcall ep8_arm ; - ret +;;; counter +;;; r0: DIOSTAT +;;; r1: counter address +;;; r2: up/down-mask +;;; r3: reset-mask +;;; r4: clock-mask +counter: + mov a,IOB ; actual IOB input state + mov r5,a ; save in r5 + anl a,r3 ; bit mask for reset + jz no_reset ; reset if one + clr a ; set counter to zero + mov @r1,a + inc r4 + mov @r1,a + sjmp ctr_end +no_reset: + mov a,@r0 ; get last state + xrl a,r5 ; has it changed? + anl a,r5 ; is it now on? + anl a,r4 ; mask out the port + jz ctr_end ; no rising edge + mov a,r5 ; get port B again + anl a,r2 ; test if up or down + jnz ctr_up ; count up + mov a,@r1 + dec a + mov @r1,a + cjne a,#0ffh,ctr_end ; underflow? + inc r1 ; high byte + mov a,@r1 + dec a + mov @r1,a + sjmp ctr_end +ctr_up: ; count up + mov a,@r1 + inc a + mov @r1,a + jnz ctr_end + inc r1 ; high byte + mov a,@r1 + inc a + mov @r1,a +ctr_end: + mov a,r5 + mov @r0,a + ret + +;;; implements two soft counters with up/down and reset +timer0_isr: + push dps + push acc + push psw + push 00h ; R0 + push 01h ; R1 + push 02h ; R2 + push 03h ; R3 + push 04h ; R4 + push 05h ; R5 + + mov r0,#DIOSTAT0 ; status of port + mov r1,#CTR0 ; address of counter0 + mov a,#00000001b ; bit 0 + mov r4,a ; clock + rl a ; bit 1 + mov r2,a ; up/down + rl a ; bit 2 + mov r3,a ; reset mask + lcall counter + inc r0 ; to DISTAT1 + inc r1 ; to CTR1 + inc r1 + mov a,r3 + rl a ; bit 3 + rl a ; bit 4 + mov r4,a ; clock + rl a ; bit 5 + mov r2,a ; up/down + rl a ; bit 6 + mov r3,a ; reset + lcall counter + + pop 05h ; R5 + pop 04h ; R4 + pop 03h ; R3 + pop 02h ; R2 + pop 01h ; R1 + pop 00h ; R0 + pop psw + pop acc + pop dps + + reti + ;;; interrupt-routine for SOF ;;; is for full speed sof_isr: @@ -438,17 +511,15 @@ sof_isr: anl a,#20H ; full? jnz epfull ; EP6-buffer is full - lcall convlo ; conversion + lcall conv_ad ; conversion mov DPTR,#EP6BCH ; byte count H mov a,#0 ; is zero - movx @DPTR,a - lcall syncdelay ; wait until we can write again + lcall syncdelaywr ; wait until we can write again mov DPTR,#EP6BCL ; byte count L mov a,#10H ; is 8x word = 16 bytes - movx @DPTR,a - lcall syncdelay ; wait until we can write again + lcall syncdelaywr ; wait until we can write again epfull: ;; do the D/A conversion @@ -460,11 +531,9 @@ epfull: lcall dalo ; conversion mov dptr,#EP2BCL ; "arm" it - mov a,#80h - movx @DPTR,a ; can receive data - lcall syncdelay ; wait for the rec to sync - movx @dptr,a ; just to make sure that it's empty - lcall syncdelay ; wait for the rec to sync + mov a,#00h + lcall syncdelaywr ; wait for the rec to sync + lcall syncdelaywr ; wait for the rec to sync epempty: ;; clear INT2 @@ -495,9 +564,34 @@ nosof: reti +reset_ep8: + ;; erase all data in ep8 + mov dptr,#FIFORESET + mov a,#80H ; NAK + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#8 ; reset EP8 + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#0 ; normal operation + lcall syncdelaywr + ret +reset_ep6: + ;; throw out old data + mov dptr,#FIFORESET + mov a,#80H ; NAK + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#6 ; reset EP6 + lcall syncdelaywr + mov dptr,#FIFORESET + mov a,#0 ; normal operation + lcall syncdelaywr + ret + ;;; interrupt-routine for ep4 ;;; receives the channel list and other commands ep4_isr: @@ -519,25 +613,74 @@ ep4_isr: mov dptr,#0f400h ; FIFO buffer of EP4 movx a,@dptr ; get the first byte + mov r0,#CMD_FLAG ; pointer to the command byte + mov @r0,a ; store the command byte for ep8 mov dptr,#ep4_jmp ; jump table for the different functions rl a ; multiply by 2: sizeof sjmp jmp @a+dptr ; jump to the jump table + ;; jump table, corresponds to the command bytes defined + ;; in usbdux.c ep4_jmp: sjmp storechannellist; a=0 sjmp single_da ; a=1 sjmp config_digital_b; a=2 sjmp write_digital_b ; a=3 + sjmp storesglchannel ; a=4 + sjmp readcounter ; a=5 + sjmp writecounter ; a=6 + + ;; read the counter +readcounter: + lcall reset_ep8 ; reset ep8 + lcall ep8_ops ; fill the counter data in there + sjmp over_da ; jump to the end + + ;; write zeroes to the counters +writecounter: + mov dptr,#0f401h ; buffer + mov r0,#CTR0 ; r0 points to counter 0 + movx a,@dptr ; channel number + jz wrctr0 ; first channel + mov r1,a ; counter +wrctrl: + inc r0 ; next counter + inc r0 ; next counter + djnz r1,wrctrl ; advance to the right counter +wrctr0: + inc dptr ; get to the value + movx a,@dptr ; get value + mov @r0,a ; save in ctr + inc r0 ; next byte + inc dptr + movx a,@dptr ; get value + mov @r0,a ; save in ctr + sjmp over_da ; jump to the end +storesglchannel: + mov r0,#SGLCHANNEL ; the conversion bytes are now stored in 80h + mov dptr,#0f401h ; FIFO buffer of EP4 + movx a,@dptr ; + mov @r0,a -;;; Channellist: + lcall reset_ep8 ; reset FIFO + ;; Save new A/D data in EP8. This is the first byte + ;; the host will read during an INSN. If there are + ;; more to come they will be handled by the ISR of + ;; ep8. + lcall ep8_ops ; get A/D data + + sjmp over_da + + +;;; Channellist: ;;; the first byte is zero: ;;; we've just received the channel list -;;; the channel list is stored in the addresses from 80H which +;;; the channel list is stored in the addresses from CHANNELLIST which ;;; are _only_ reachable by indirect addressing storechannellist: mov r0,#CHANNELLIST ; the conversion bytes are now stored in 80h - mov r2,#8 ; counter + mov r2,#9 ; counter mov dptr,#0f401h ; FIFO buffer of EP4 chanlloop: movx a,@dptr ; @@ -545,9 +688,15 @@ chanlloop: inc dptr inc r0 djnz r2,chanlloop - clr a ; announce analogue transaction - mov r0,#DIOFLAG ; pointer to the command byte - mov @r0,a ; set the command byte + + lcall reset_ep6 ; reset FIFO + + ;; load new A/D data into EP6 + ;; This must be done. Otherwise the ISR is never called. + ;; The ISR is only called when data has _left_ the + ;; ep buffer here it has to be refilled. + lcall ep6_arm ; fill with the first data byte + sjmp over_da ;;; Single DA conversion. The 2 bytes are in the FIFO buffer @@ -556,7 +705,7 @@ single_da: lcall dalo ; conversion sjmp over_da -;;; configure the port B +;;; configure the port B as input or output (bitwise) config_digital_b: mov dptr,#0f401h ; FIFO buffer of EP4 movx a,@dptr ; get the second byte @@ -572,22 +721,25 @@ write_digital_b: inc dptr ; next byte movx a,@dptr ; bits mov IOB,a ; send the byte to the I/O port - mov a,#0ffh ; announce DIO transaction - mov r0,#DIOFLAG ; pointer to the command byte - mov @r0,a ; set the command byte - sjmp over_da -;;; more things here to come... - + lcall reset_ep8 ; reset FIFO of ep 8 + + ;; fill ep8 with new data from port B + ;; When the host requests the data it's already there. + ;; This must be so. Otherwise the ISR is not called. + ;; The ISR is only called when a packet has been delivered + ;; to the host. Thus, we need a packet here in the + ;; first instance. + lcall ep8_ops ; get digital data + + ;; + ;; for all commands the same over_da: mov dptr,#EP4BCL - mov a,#80h - movx @DPTR,a ; arm it - lcall syncdelay ; wait - movx @DPTR,a ; arm it - lcall syncdelay ; wait - movx @DPTR,a ; arm it - lcall syncdelay ; wait + mov a,#00h + lcall syncdelaywr ; arm + lcall syncdelaywr ; arm + lcall syncdelaywr ; arm ;; clear INT2 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request @@ -678,29 +830,57 @@ noDA: ret ;;; arm ep6 ep6_arm: - lcall convlo + lcall conv_ad mov DPTR,#EP6BCH ; byte count H mov a,#0 ; is zero - movx @DPTR,a - lcall syncdelay ; wait until the length has arrived + lcall syncdelaywr ; wait until the length has arrived mov DPTR,#EP6BCL ; byte count L mov a,#10H ; is one - movx @DPTR,a - lcall syncdelay ; wait until the length has been proc + lcall syncdelaywr ; wait until the length has been proc ret ;;; converts one analog/digital channel and stores it in EP8 -;;; also gets the content of the digital ports B and D -ep8_adc: - mov r0,#DIOFLAG ; pointer to the DIO flag - mov a,@r0 ; get the flag - jnz ep8_dio ; nonzero means DIO +;;; also gets the content of the digital ports B and D depending on +;;; the COMMAND flag +ep8_ops: + mov r0,#CMD_FLAG + mov a,@r0 - mov r0,#CHANNELLIST ; points to the channellist + mov dptr,#ep8_jmp ; jump table for the different functions + rl a ; multiply by 2: sizeof sjmp + jmp @a+dptr ; jump to the jump table + ;; jump table, corresponds to the command bytes defined + ;; in usbdux.c +ep8_jmp: + sjmp ep8_err ; a=0, err + sjmp ep8_err ; a=1, err + sjmp ep8_err ; a=2, err + sjmp ep8_dio ; a=3, digital read + sjmp ep8_sglchannel ; a=4, analog A/D + sjmp ep8_readctr ; a=5, read counter + sjmp ep8_err ; a=6, write counter + + ;; reads all counters +ep8_readctr: + mov r0,#CTR0 ; points to counter0 + mov dptr,#0fc00h ; ep8 fifo buffer + mov r1,#8 ; transfer 4 16bit counters +ep8_ctrlp: + mov a,@r0 ; get the counter + movx @dptr,a ; save in the fifo buffer + inc r0 ; inc pointer to the counters + inc dptr ; inc pointer to the fifo buffer + djnz r1,ep8_ctrlp ; loop until ready + + sjmp ep8_send ; send the data + + ;; read one A/D channel +ep8_sglchannel: + mov r0,#SGLCHANNEL ; points to the channel mov a,@r0 ; Ch0 lcall readAD ; start the conversion @@ -714,6 +894,7 @@ ep8_adc: sjmp ep8_send ; send the data + ;; read the digital lines ep8_dio: mov DPTR,#0fc00h ; store the contents of port B mov a,IOB ; in the next @@ -726,27 +907,14 @@ ep8_dio: ep8_send: mov DPTR,#EP8BCH ; byte count H mov a,#0 ; is zero - movx @DPTR,a + lcall syncdelaywr mov DPTR,#EP8BCL ; byte count L mov a,#10H ; 16 bytes - movx @DPTR,a ; send the data over to the host - ret - + lcall syncdelaywr ; send the data over to the host - -;;; arms EP8 with one byte. This signals the Linux driver that -;;; the EP has been armed only with a dummy byte to make the -;;; IRQ work. The byte is not processed by the driver. -ep8_arm: - mov DPTR,#EP8BCH ; byte count H - mov a,#0 ; is zero - movx @DPTR,a - - mov DPTR,#EP8BCL ; byte count L - mov a,#1 ; 1 byte - movx @DPTR,a - ret +ep8_err: + ret @@ -770,7 +938,7 @@ ep8_isr: push 06h ; R6 push 07h ; R7 - lcall ep8_adc + lcall ep8_ops ;; clear INT2 mov a,EXIF ; FIRST clear the USB (INT2) interrupt request @@ -807,6 +975,16 @@ syncdelay: nop nop nop + nop + nop + nop + nop + nop + ret + +syncdelaywr: + movx @dptr,a + lcall syncdelay ret diff --git a/etc/hotplug/usb/usbdux/usbdux_firmware.hex b/etc/hotplug/usb/usbdux/usbdux_firmware.hex index bb3da3e..366bff4 100644 --- a/etc/hotplug/usb/usbdux/usbdux_firmware.hex +++ b/etc/hotplug/usb/usbdux/usbdux_firmware.hex @@ -1,66 +1,80 @@ :030000000201A258 +:03000B000202F8F6 :03004300020100B7 -:1001000002017F000202EF0002017F0002017F0076 +:1001000002017F000203390002017F0002017F002B :1001100002017F0002017F0002017F0002017F00D7 :1001200002017F0002017F0002017F0002017F00C7 -:1001300002017F0002036C0002017F0002049900AB +:1001300002017F000203E40002017F0002055A0071 :1001400002017F0002017F0002017F0002017F00A7 :1001500002017F0002017F0002017F0002017F0097 :1001600002017F0002017F0002017F0002017F0087 :1001700002017F0002017F0002017F0002017FC0B7 :1001800086C082C083C084C085C0E0C0D0E591C273 :10019000E4F591D0D0D0E0D085D084D083D082D087 -:1001A000863290E6007410F01204E590E668740858 -:1001B000F01204E51201C3120288000000000000E2 -:1001C0000080F775B22775802222547C4481C28159 -:1001D0007A0830E704D2828002C282D280C28023B1 -:1001E000DAF0C2827A05D280C280DAFA7C007A0420 -:1001F0007D08D280C280E58030E403EC4DFCED0345 -:10020000FDDAEF7B007A087D80D280C280E5803005 -:10021000E403EB4DFBED03FDDAEFD28122759AF892 -:10022000759B0075AF077880E61201CAEB90E67BFC -:10023000F0ECF008E61201CAEB90E67BF0ECF00877 -:10024000E61201CAEB90E67BF0ECF008E61201CA78 -:10025000EB90E67BF0ECF008E61201CAEB90E67B4F -:10026000F0ECF008E61201CAEB90E67BF0ECF00847 -:10027000E61201CAEB90E67BF0ECF008E61201CA48 -:10028000EB90E67BF0ECF02290E604740FF0740043 -:10029000F090E6127492F090E6187400F090E691F7 -:1002A0007480F01204E5F01204E5F01204E590E623 -:1002B0001374A0F090E6197400F090E6957480F045 -:1002C0001204E5F01204E590E61474D2F090E615FD -:1002D00074E0F090E65E74A0F090E65F74A0F09099 -:1002E000E65C7402F075E80175A88012048C22C0E7 -:1002F00086C082C083C084C085C0E0C0D0C000C0BA -:1003000001C002C003C004C005C006C007E5AA546E -:1003100020701512021D90E6987400F01204E5900A -:10032000E6997410F01204E5E5AA5401701390F0F8 -:100330000012040B90E6917480F01204E5F01204B0 -:10034000E5E591C2E4F59190E65D7402F0D007D046 -:1003500006D005D004D003D002D001D000D0D0D038 -:10036000E0D085D084D083D082D08632C086C0824F -:10037000C083C084C085C0E0C0D0C000C001C0023E -:10038000C003C004C005C006C00790F400E090039D -:1003900093237380068017801D802378807A0890CD -:1003A000F401E0F6A308DAFAE47890F6802190F4FC -:1003B0000112040B801990F401E0F5B3801190F460 -:1003C00001E0F5B3A3E0F59074FF7890F68000901B -:1003D000E6957480F01204E5F01204E5F01204E5ED -:1003E000E591C2E4F59190E65F7420F0D007D00665 -:1003F000D005D004D003D002D001D000D0D0D0E0BE -:10040000D085D084D083D082D08632E0A3F8E0FBC0 -:10041000A3E0FCA3E0A312041CD8F32254C0443090 -:100420004CC2857A0830E704D2828002C282D28030 -:10043000C28023DAF0EB7A0830E704D2828002C26D -:1004400082D280C28023DAF0D2852212021D90E689 -:10045000987400F01204E590E6997410F01204E527 -:10046000227890E670107880E61201CA90FC00EBCA -:10047000F0A3ECF0800990FC00E590F0A3E4F0908C -:10048000E69C7400F090E69D7410F02290E69C7457 -:1004900000F090E69D7401F022C086C082C083C047 -:1004A00084C085C0E0C0D0C000C001C002C003C08D -:1004B00004C005C006C007120461E591C2E4F591CD -:1004C00090E65F7480F0D007D006D005D004D0034A -:1004D000D002D001D000D0D0D0E0D085D084D0835D -:0A04E000D082D08632000000002216 +:1001A000863290E60074101205B090E60B740312CC +:1001B00005B075B80075F8FF90E66874081205B0D0 +:1001C0001201D212025A1201D900000000000000F0 +:1001D00080F775B22775802222758E01758A107599 +:1001E0008C0075A882758900D28C22547C4481C20F +:1001F000817A0830E704D2828002C282D280C28033 +:1002000023DAF0C2827A05D280C280DAFA7C007AE0 +:10021000047D08D280C280E58030E403EC4DFCED23 +:1002200003FDDAEF7B007A087D80D280C280E58012 +:1002300030E403EB4DFBED03FDDAEFD28122759A3A +:10024000F8759B0075AF077880E6F990E67B08E6C5 +:100250001201EBEBF0ECF0D9F52290E6047480F09B +:100260007402F07404F07406F07408F07400F090F6 +:10027000E6127492F090E6187400F090E691740023 +:10028000F01205A6F01205A6F01205A690E613746A +:10029000A0F090E6197400F090E6957400F0120555 +:1002A000A6F01205A690E61474D2F090E61574E05C +:1002B000F090E65E74A0F090E65F74A0F090E65CCB +:1002C0007402F075E80175A88022E590FD5B600678 +:1002D000E4F70CF7801FE66D5D5C6019ED5A700C59 +:1002E000E714F7B4FF0F09E714F78009E704F77084 +:1002F0000409E704F7EDF622C086C0E0C0D0C000D4 +:10030000C001C002C003C004C005789879A0740180 +:10031000FC23FA23FB1202CA080909EB2323FC235E +:10032000FA23FB1202CAD005D004D003D002D001B8 +:10033000D000D0D0D0E0D08632C086C082C083C08A +:1003400084C085C0E0C0D0C000C001C002C003C0EE +:1003500004C005C006C007E5AA5420701312023E6F +:1003600090E69874001205B090E69974101205B0EA +:10037000E5AA5401701190F0001204B790E6917450 +:10038000001205B01205B0E591C2E4F59190E65D6A +:100390007402F0D007D006D005D004D003D002D02C +:1003A00001D000D0D0D0E0D085D084D083D082D00E +:1003B000863290E60474801205B090E60474081248 +:1003C00005B090E60474001205B02290E604748033 +:1003D0001205B090E60474061205B090E6047400AD +:1003E0001205B022C086C082C083C084C085C0E030 +:1003F000C0D0C000C001C002C003C004C005C00618 +:10040000C00790F400E07890F690040E23738039D2 +:10041000804C805280588022800280081203B212E1 +:10042000050B805A90F40178A0E06005F90808D91E +:10043000FCA3E0F608A3E0F68044789190F401E094 +:10044000F61203B212050B803578807A0990F40118 +:10045000E0F6A308DAFA1203CB1204F7802090F436 +:10046000011204B7801890F401E0F5B3801090F405 +:1004700001E0F5B3A3E0F5901203B212050B90E68C +:100480009574001205B01205B01205B0E591C2E4F2 +:10049000F59190E65F7420F0D007D006D005D00427 +:1004A000D003D002D001D000D0D0D0E0D085D0840D +:1004B000D083D082D08632E0A3F8E0FBA3E0FCA397 +:1004C000E0A31204C8D8F32254C044304CC2857A49 +:1004D0000830E704D2828002C282D280C28023DA4E +:1004E000F0EB7A0830E704D2828002C282D280C266 +:1004F0008023DAF0D2852212023E90E69874001230 +:1005000005B090E69974101205B0227890E6900537 +:1005100013237380448042804080258013800280B2 +:100520003878A090FC007908E6F008A3D9FA801981 +:100530007891E61201EB90FC00EBF0A3ECF080095F +:1005400090FC00E590F0A3E4F090E69C74001205A6 +:10055000B090E69D74101205B022C086C082C083A0 +:10056000C084C085C0E0C0D0C000C001C002C003CC +:10057000C004C005C006C00712050BE591C2E4F532 +:100580009190E65F7480F0D007D006D005D004D0FB +:1005900003D002D001D000D0D0D0E0D085D084D01C +:1005A00083D082D0863200000000000000000022CC +:0505B000F01205A62277 :00000001FF