; 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
; 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
;
;;;
.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
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
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
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...
-;;; 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
;;; 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
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
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
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:
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
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
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:
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 ;
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
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
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
;;; 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
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
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
push 06h ; R6
push 07h ; R7
- lcall ep8_adc
+ lcall ep8_ops
;; clear INT2
mov a,EXIF ; FIRST clear the USB (INT2) interrupt request
nop
nop
nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ ret
+
+syncdelaywr:
+ movx @dptr,a
+ lcall syncdelay
ret
: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