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.
This commit is contained in:
Frank Mori Hess 2004-08-01 22:24:01 +00:00
parent 2afaa069bd
commit cdb4e7d266
2 changed files with 386 additions and 194 deletions

View file

@ -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 <Bernd.Porr@cn.stir.ac.uk>
; Updated: 25 Jan 2004
; Author: Bernd Porr <Bernd.Porr@f2s.com>
; 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
mov a,@r0 ; number of channels
mov r1,a ; counter
mov DPTR,#XAUTODAT1 ; auto pointer
convloop:
inc r0
mov a,@r0 ;Ch2
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 ;Ch3
lcall readAD
mov a,R3 ;
mov DPTR,#XAUTODAT1
movx @DPTR,A
mov a,R4 ;
movx @DPTR,A
inc r0
mov a,@r0 ;Ch4
lcall readAD
mov a,R3 ;
mov DPTR,#XAUTODAT1
movx @DPTR,A
mov a,R4 ;
movx @DPTR,A
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
;;; Channellist:
;; 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
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,29 +907,16 @@ 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
lcall syncdelaywr ; send the data over to the host
ep8_err:
ret
;;; 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 interrupt: gets one measurement from the AD converter and
;;; sends it via EP8. The channel # is stored in address 80H.
@ -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

View file

@ -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