pax_global_header00006660000000000000000000000064146634454600014526gustar00rootroot0000000000000052 comment=b6066a292845bad70cb53c4c53e24e3d35ea1d0d nxt-firmware-1.29.7/000077500000000000000000000000001466344546000142515ustar00rootroot00000000000000nxt-firmware-1.29.7/.build.yml000066400000000000000000000026001466344546000161470ustar00rootroot00000000000000image: debian/stable packages: - gcc-arm-none-eabi - picolibc-arm-none-eabi - wget - unzip - nbc - python3-pil sources: - https://git.sr.ht/~ni/nxt-firmware environment: arm_url: https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz?rev=e434b9ea4afc4ed7998329566b764309&hash=688C370BF08399033CA9DE3C1CC8CF8E31D8C441 arm_file: arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi.tar.xz arm_dir: arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi picolibc_url: https://keithp.com/picolibc/dist/gnu-arm-embedded/picolibc-1.8.6-13.2.Rel1.zip picolibc_file: picolibc-1.8.6-13.2.Rel1.zip artifacts: - nxt_firmware.rfw tasks: - build-debian: | cd nxt-firmware arm-none-eabi-gcc --version make cp nxt_firmware.bin ~/nxt_firmware.rfw make clean - build-arm: | # Only on build branch. if [ "$GIT_REF" = "refs/heads/build-arm" ]; then \ wget --no-verbose -O $arm_file "$arm_url"; \ wget --no-verbose -O $picolibc_file "$picolibc_url"; \ tar xaf $arm_file; \ cd $HOME/$arm_dir; \ unzip -q $HOME/$picolibc_file; \ PATH=$HOME/$arm_dir/bin:$PATH; \ cd $HOME/nxt-firmware; \ arm-none-eabi-gcc --version; \ make; \ make clean; \ fi - build-tests: | cd nxt-firmware make -C tests build nxt-firmware-1.29.7/.gitignore000066400000000000000000000001131466344546000162340ustar00rootroot00000000000000*.[oad] *.lst *.sym *.elf *.bin *.rfw /*.h tags version.mak *.tar.gz *.asc nxt-firmware-1.29.7/LEGO MINDSTORMS NXT Firmware Open Source.doc000066400000000000000000022500001466344546000234370ustar00rootroot00000000000000ࡱ>   @ bjbj\\/  ---8-D..T H.$////>6@7L7(&(((((($RAL 75"5|77L //a>>>7 l/ R/&>7&>>V V , "/. P"#-="v<$w0=x d77>77777LL +$>" +  SHAPE \* MERGEFORMAT  Table of contentS  TOC \o "1-3" \h \z \t "LMS overskrift 2;2;LMS overskrift 1;1;LMS overskrift 3;3"  HYPERLINK \l "_Toc141076595" Table of contentS  PAGEREF _Toc141076595 \h 2  HYPERLINK \l "_Toc141076596" Introduction  PAGEREF _Toc141076596 \h 3  HYPERLINK \l "_Toc141076597" Firmware distribution  PAGEREF _Toc141076597 \h 4  HYPERLINK \l "_Toc141076598" AT91SAM7S256 Processor depended files  PAGEREF _Toc141076598 \h 5  HYPERLINK \l "_Toc141076599" AT91SAM7S256 Source code  PAGEREF _Toc141076599 \h 5  HYPERLINK \l "_Toc141076600" ATmega48 Processor depended files  PAGEREF _Toc141076600 \h 8  HYPERLINK \l "_Toc141076601" ATmega48 Source code  PAGEREF _Toc141076601 \h 8  Introduction This document describes briefly what is included within the LEGO MINDSTORMS NXT Firmware Open Source. The Open Source agreement relates to firmware within the ATMEL AT91SAM7s256 processor and the ATMEL ATmega48 microcontroller within the LEGO MINDSTORMS NXT brick. Firmware distribution The firmware is divided into two main folder one for each of the processor types. Within each of the folders the firmware is divided into folders which includes the source files and folder which includes the processor related files. The firmware for the ATMEL AT91SAM7S256 is build using the IAR Embedded Workbench 4.0 using compiler version 4.20A. The firmware for the ATMEL ATmega48 AVR is build using AVR Studio 4.0. AT91SAM7S256 Processor depended files The following processor depended files are included within the Open Source license for the LEGO MINDSTORMS NXT, ATMEL AT91SAM7S256 processor. Yvals.h AT91SAM7S256.h AT91SAM7S256_inc.h CStartup.s79 Cstartup_SAM7.c Ctype.h DLib_Defaults.h Dlib_Product.h ioat91sam7s256.h lib_AT91SAM7S256.h Sam7s256.c Stdbool.h Stdio.h Stdlib.h String.h Time.h Wchar.h xencoding_limits.h xlocale.h xlocale_c.h xlocaleuse.h Xmtx.h Xtinfo.h Xtls.h YSizet.h AT91SAM7S256 Source code The following source files are included within the Open Source license for the LEGO MINDSTORMS NXT, ATMEL AT91SAM7S256 processor. c_button.c c_cmd.c c_comm.c c_display.c c_input.c c_ioctrl.c c_loader.c c_lowspeed.c c_output.c c_sound.c c_ui.c d_bt.c d_button.c d_display.c d_hispeed.c d_input.c d_ioctrl.c d_loader.c d_lowspeed.c d_output.c d_sound.c d_timer.c d_usb.c m_sched.c c_button.h c_cmd.h c_cmd_bytecode.h c_comm.h c_display.h c_input.h c_ioctrl.h c_loader.h c_lowspeed.h c_output.h c_sound.h c_ui.h d_bt.h d_button.h d_display.h d_hispeed.h d_input.h d_ioctrl.h d_loader.h d_lowspeed.h d_output.h d_sound.h d_timer.h d_usb.h m_sched.h modules.h stdconst.h BtTest.inc c_cmd_drawing.inc Functions.inl c_button.iom c_cmd.iom c_comm.iom c_display.iom c_input.iom c_ioctrl.iom c_loader.iom c_lowspeed.iom c_output.iom c_sound.iom c_ui.iom d_bt.r d_button.r d_display.r d_hispeed.r d_input.r d_ioctrl.r d_loader.r d_lowspeed.r d_output.r d_sound.r d_timer.r d_usb.r MainMenu.rms Submenu01.rms Submenu02.rms Submenu03.rms Submenu04.rms Submenu05.rms Submenu06.rms Submenu07.rms Connection.txt Cursor.txt Devices.txt Display.txt Fail.txt Font.txt Icons.txt Info.txt LowBattery.txt Ok.txt Port.txt RCXintro_1.txt RCXintro_2.txt RCXintro_3.txt RCXintro_4.txt RCXintro_5.txt RCXintro_6.txt RCXintro_7.txt RCXintro_8.txt RCXintro_9.txt RCXintro_10.txt RCXintro_11.txt RCXintro_12.txt RCXintro_13.txt RCXintro_14.txt RCXintro_15.txt RCXintro_16.txt Running.txt Status.txt Step.txt Test1.txt Test2.txt Ui.txt Wait.txt ATmega48 Processor depended files The following processor depended files are included within the Open Source license for the LEGO MINDSTORMS NXT, ATMEL ATmega48 processor. atmega48.c atmega48.h inavr.h iom48.h iomacro.h cl1s-ec.r90 ATmega48 Source code The following source files are included within the Open Source license for the LEGO MINDSTORMS NXT, ATMEL ATmega48 processor. c_armcomm.c d_armcomm.c d_button.c d_input.c d_output.c d_pccomm.c d_power.c d_timer.c m_sched.c c_armcomm.h d_armcomm.h d_button.h d_input.h d_output.h d_pccomm.h d_power.h d_timer.h m_sched.h stdconst.h d_armcomm.r d_button.r d_input.r d_output.r d_pccomm.r d_power.r d_timer.r   LEGO MINDSTORMS NXT Firmware Open Source 2006 The LEGO Group  PAGE 3   LEGO MINDSTORMS NXT Firmware Open Source LEGO, the LEGO logo, MINDSTORMS and the MINDSTORMS logo are trademarks of the LEGO Group 2006 The LEGO Group Other product and company names listed are trademarks or trade names of their respective companies. LEGO MINDSTORMS NXT Firmware Open Source Version: 1.00 Version: 1.00 1234ɽqaVGVjhUmHnHuhmHnHuhh0JmHnHsHu2jhh>*B*UmHnHphuhmHnHuhh0JmHnHu$jhh0JUmHnHuhsmHsHjhsUmHsHhs5;CJ \aJ mH sH hvhhsmHsHjhsUmHnHtHujhsUhs23* E    x   5  gdKgdIK$a$gdpKp$a$gd %gdsgds  %gds\ $ % & ' ( ) * + , H I J K ` a ªœyœhªœNœ2jhh>*B*UmHnHphu jhUmHnHu2jhh>*B*UmHnHphuhmHnHuhh0JmHnHu.h5;OJQJ\^JmHnHsH tH u$jhh0JUmHnHuhmHnHujhUmHnHu j hUmHnHua b | } ~   xgM2jxhh>*B*UmHnHphu jhUmHnHu2j~hh>*B*UmHnHphuhmHnHuhh0JmHnHu.h5;OJQJ\^JmHnHsH tH u$jhh0JUmHnHu jhUmHnHuhmHnHujhUmHnHu # $ % ? @ A B C D E F G c d e f ׳xg׳ jhUmHnHu2jrhh>*B*UmHnHphuhmHnHu.h5;OJQJ\^JmHnHsH tH u$jhh0JUmHnHu jhUmHnHujhUmHnHuhmHnHuhh0JmHnHu      ) j w x źӂvk`\UNJF\FB>h>h h M+h#F h~?hs h1hshsh4|hsmH sH hvhhsmHsHjhsUmHsH.h5;OJQJ\^JmHnHsH tH u jhUmHnHujhUmHnHuhmHnHuhh0JmHnHu$jhh0JUmHnHu2jlhh>*B*UmHnHphu    & 4 5 $ - H $Vs3?IR[\]^bcƾ䝕jhDMhsUjfhsU"jhsUmHnHsH tHuh"hDShKmHsHhKmHsHh77 hChK hhKhhIKhKhpKph&(hruhsh M+h>h 6%0:BKT[cvgdK & FgdK & FgdIKVWbjs*4>FP & FgdKgdKgdIKgdIKP[ct})4>HPZdoz & FgdKz !(3?KU`kx & FgdK(4@IR\et{ +; & FgdK;K[k{mny>?KWb & Fgd"gd"gd" & FgdKblw #-8CMWXYZ[gd" & Fgd"[\_`abd:Ogds D%Bgdsgdsgds$a$gdpKpchituz:;鱠vhg0JmH sH hDMhs0JmH sH  h0ehshShsH*hg hg;%hsOJQJ^JmH sH hg;%hsmH sH "jhsUmHnHsH tHuhgmHnHujhDMhsU h]hshsh#FhDMhsH* hDMhs,$a$gdpKpgds$a$gdshpKph#Fhghg0JmH sH hDMhs0JmH sH hs h$jvhs 5 0&P 1h:ps. A!n"n#S$S%SSFj(S0ЯcOBPFJFIF ExifMM*bj(1r2iAdobe Photoshop CS Macintosh2006:06:13 16:40:47!&(. HHJFIFHH Adobe_CMAdobed            y"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?TI%,e-R1!6)yM"mcR4)IM=-IILML%8'Is:tjk=~ T{ң b"f~kî.Ys@?Kfff4MΦ669ogGqs5Q1lջݷo{?UxVTI%,yH7)cIJ]i G*&$7t4xImR?LI_'i%>*٘XUNVa 3Шx cz!]#U(uN$Iۀ-~*.. < D=֏|7^)Ns +Gt{݀]iv;sRjIs౗63&8-3q抛k|STI%,xM')RM:R?$xIL]-qbtNgۨRO?R~i@~& z)%mp%<R`F~<B"Hb9B<*<<STI%)G2JQh$GDpe4wL51&a%1p;?*=61))m8> ZNꗵ(ֹHП$Mm]:'cn:F.%;b#Oz/qD$92I)TI%,xL撖14-ORu%(<2ѻp3})w'yO)7<|yqh2uŸN)IHH$c1]LA0`S!%1۝]VL:[>|Qlκ*i@:JTI%,xLJ[dd %,A?.Im^8%#p}u:j{^ɒ i#<{$ gQZB!I:HA^ =|j,L":IUPx$TI%)11쒖R/I=)Fh2<RjF֝6dq'C{m=%,qdu'_qS: GIMt?$fŤ|NtK'I*mklhqFrJH8 L%?TI%,XO&J_7i?$TChOߔڑ⒕7Dӂ$j ?%1> ܐٸ"B i:SJuPdlsvF?"֖KSTILa/S)hJWt5 Ӥ$iLwhyRJJXƉ`H29ʌ5 )Pr com.apple.print.PageFormat.PMHorizontalRes com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMHorizontalRes 72 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2005-07-14T13:03:37Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMOrientation com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMOrientation 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2005-07-14T13:03:37Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMScaling com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMScaling 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2005-07-14T13:03:37Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalRes com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalRes 72 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2005-07-14T13:03:37Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMVerticalScaling com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMVerticalScaling 1 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2005-07-14T13:03:37Z com.apple.print.ticket.stateFlag 0 com.apple.print.subTicket.paper_info_ticket com.apple.print.PageFormat.PMAdjustedPageRect com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPageRect 0.0 0.0 783 559 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2006-06-13T14:40:06Z com.apple.print.ticket.stateFlag 0 com.apple.print.PageFormat.PMAdjustedPaperRect com.apple.print.ticket.creator com.apple.printingmanager com.apple.print.ticket.itemArray com.apple.print.PageFormat.PMAdjustedPaperRect -18 -18 824 577 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2006-06-13T14:40:06Z com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMPaperName com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMPaperName iso-a4 com.apple.print.ticket.client com.apple.print.pm.PostScript com.apple.print.ticket.modDate 2003-07-01T17:49:36Z com.apple.print.ticket.stateFlag 1 com.apple.print.PaperInfo.PMUnadjustedPageRect com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPageRect 0.0 0.0 783 559 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2005-07-14T13:03:37Z com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.PMUnadjustedPaperRect com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.PMUnadjustedPaperRect -18 -18 824 577 com.apple.print.ticket.client com.apple.printingmanager com.apple.print.ticket.modDate 2005-07-14T13:03:37Z com.apple.print.ticket.stateFlag 0 com.apple.print.PaperInfo.ppd.PMPaperName com.apple.print.ticket.creator com.apple.print.pm.PostScript com.apple.print.ticket.itemArray com.apple.print.PaperInfo.ppd.PMPaperName A4 com.apple.print.ticket.client com.apple.print.pm.PostScript com.apple.print.ticket.modDate 2003-07-01T17:49:36Z com.apple.print.ticket.stateFlag 1 com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.privateLock com.apple.print.ticket.type com.apple.print.PaperInfoTicket com.apple.print.ticket.APIVersion 00.20 com.apple.print.ticket.privateLock com.apple.print.ticket.type com.apple.print.PageFormatTicket 8BIMxHH/8Ag{HH(dh 8BIM8BIM&?8BIM x8BIM8BIM 8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM8BIM8BIM$@@T [o`8BIM8BIMS!Background_grey!nullboundsObjcRct1Top longLeftlongBtomlong!RghtlongslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlong!RghtlongurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIM8BIM yl JFIFHH Adobe_CMAdobed            y"?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?TI%,e-R1!6)yM"mcR4)IM=-IILML%8'Is:tjk=~ T{ң b"f~kî.Ys@?Kfff4MΦ669ogGqs5Q1lջݷo{?UxVTI%,yH7)cIJ]i G*&$7t4xImR?LI_'i%>*٘XUNVa 3Шx cz!]#U(uN$Iۀ-~*.. < D=֏|7^)Ns +Gt{݀]iv;sRjIs౗63&8-3q抛k|STI%,xM')RM:R?$xIL]-qbtNgۨRO?R~i@~& z)%mp%<R`F~<B"Hb9B<*<<STI%)G2JQh$GDpe4wL51&a%1p;?*=61))m8> ZNꗵ(ֹHП$Mm]:'cn:F.%;b#Oz/qD$92I)TI%,xL撖14-ORu%(<2ѻp3})w'yO)7<|yqh2uŸN)IHH$c1]LA0`S!%1۝]VL:[>|Qlκ*i@:JTI%,xLJ[dd %,A?.Im^8%#p}u:j{^ɒ i#<{$ gQZB!I:HA^ =|j,L":IUPx$TI%)11쒖R/I=)Fh2<RjF֝6dq'C{m=%,qdu'_qS: GIMt?$fŤ|NtK'I*mklhqFrJH8 L%?TI%,XO&J_7i?$TChOߔڑ⒕7Dӂ$j ?%1> ܐٸ"B i:SJuPdlsvF?"֖KSTILa/S)hJWt5 Ӥ$iLwhyRJJXƉ`H29ʌ5 )Pr 4294967295 994 1313 1 150/1 150/1 2 2006-06-13T16:40:47+02:00 2006-06-13T16:40:47+02:00 2006-06-13T16:40:47+02:00 Adobe Photoshop CS Macintosh uuid:a8cfac5a-fc8d-11da-ad44-d88e326f2959 adobe:docid:photoshop:912b4127-fc6c-11da-ad44-d88e326f2959 adobe:docid:photoshop:a8cfac5f-fc8d-11da-ad44-d88e326f2959 image/jpeg @ICC_PROFILE0ADBEmntrRGB XYZ acspAPPLnone-ADBE cprt2desc0kwtptbkptrTRCgTRCbTRCrXYZgXYZbXYZtextCopyright 1999 Adobe Systems IncorporateddescAdobe RGB (1998)XYZ QXYZ curv3curv3curv3XYZ OXYZ 4,XYZ &1/Adobed@!}     u!"1A2# QBa$3Rqb%C&4r 5'S6DTsEF7Gc(UVWdte)8fu*9:HIJXYZghijvwxyzm!1"AQ2aqB#Rb3 $Cr4%ScD&5T6Ed' sFtUeuV7)(GWf8vgwHXhx9IYiy*:JZjz ?ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߛ[{^-K_8>:XHXv#Ϭy J0WboJPu3Z7:XeGr[Hl X{zI>NuWs596?^N}{.Mڮ/o5R@{]w{{^׺u{{^׺u{{^ <~C9[뎦"q`mߺ]sIuk׺^ֺ9C'ߺX9 u{wksoXֺuߺ^~q}?>׺͕cǽ{Ӂ[Z׺,5[p}}rcu}}u>Iu=u@Xm>ï׺ȱ8|y޺Y^땽u߿u{fER?#zt끜61by#_׽$s=u1~﾿~r׽u~{ߺ^׽u~pc`O~?{[.~>׫s:mױ׵O~]u=odBK97zMOߺ]#׿uh$}?~s u=uˀ?x}uȣ?}=-{G׺Ȍ[}y?~ֺ+ۏ?[{^{Zm}um}u֢?'ߺ^s^\?~׺?r8[?u{{^׺u{{^׺^p?#uIz_ߺ\ǿu׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^K^_~ԯuo{^׺u{:\>u{^Ψly~=b5kFcouupont{?{zjO{Z~{ߺ^׽u~c {Ϩ<p?ۃ]ouu{ы"YGӃa:y׸6}pzrTu5K޽u??z}u{]z׺>c?:QױZ{^뉐#P^1cnmuu! #{35ߏ: Ͻun}{^Oq}{}??#do/>ֺߺ^׽u~{ߺ^׽uV">@/kGk\؟ש%[ߺYD^2§7mo~f{Nu"s{ TN>߸uu~?cu?W߿u'5=vY}tRm{ڇsWPmu_~^N~[^c뎣AԘ~{A}kߺ\u~{ߺ^׽u^G}/6_ޫe-׹'}"ߺY}u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ /#U6@{:\d$A֫_~}@Ef {\{qէ^\>Sb m_nqQ)1..Ė xbG~/]{o{^]3:ԏzG_^?}u@M׼ӺX{Z~{ߺ^׽u~`^/uϽo9 .@/Ͽu}AyJnOoͿ؟~}p?׺o{^d?7z`xƿ^׏R7zuquoȰ{^됄?8K>׺a?~?؟{]d c{~o^9* OuuG&k~?׺o{}䋞?^O^4{zA7O~[^Cu=HğocuT?"ۏ7aǭt@^뗔Oվnw[~GϿSW׫Ԉcqï}k{^׺u{{^׺)}WVf'^눈OmT*~{sazwt^@c{^뉙G>׺\9'=u{ae񯯿u'nuީ׫׋X =u{={{^zN?F:F,/8) ׺hy517]oˮ~퇿uߺzO~ ~׫׵sozsE,ߞ _'ߺߗN1!6_{M׺ߺ^^=uȣ~c3׺_[oU}a3_+{~B}u}뭎F9Ե=u߿u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺPؐf#׺@?Q~tG{^ڔq{[q{&U?ߺXu[ߺX*'n_zHkN[sj<6㋐>]zq{^:/OSz;ҭ[_<{U=wRc6{WߺM!=D 'zG^_k}{^\bAͿ=cϯ\gyu2_֏N l`O~/z!pGԏWjG#׫׃_zv={Zq?zk{^׺u{{^׺ ~b.̀{mP uu97?^N?Co{}c5y?]{ujq?~ϿyS\**$^2Ē m]Zu[ߺO^?[ߺz{놿C:~m]{=R'{pXZ骲][_{z:u1$ OױeQ?NԤqߺR"LZp>~]g X {^`O~CP؞>~?k{F_cF?_~ï?刺uu0?Co=_=zvqL@?>{oߺYu~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺM"jcqԛ[UZ:PnWX<~`8޺S.J1>7#?{ꦼ:d $ou~~RG?}o~‡^'/q~8uǭxTFZ_{'4׋ Mͭċ_={Mӎ?qY.bIN 7ys"ǟ-a{MNֺu{{^׺uO:Eֽʹ=ˬ+Lo׏$~dk}sZe?[[p-N\!T7'ߺ5)}u/xO={O~^~"sרzu")<`ZL!~{^$$z,>,?Wz:'zq׈zg-kGZ#Ӯ3[ormoz{zP-?>ֺkP_ %}u b~=]caaca>ױ^\qbl>&LuO>z@]kJEvDq؁#bx [zn?O_^G]ͬ-שpO~9JtD OR6_~kssȵuF݇N5k}-z*E3X~}꣭Pu":y.#OutL G[Ͽu{^׺YG{^J\R#Z{^R`ߺX%&>׺bj~}[r~-ǿuy7>}?ozwO~U)?Hz~iRm`lGozjH<~ՀX׳z׳׵{8cu{H:{Wֱ߱Җo/~Gӽ{['|9,qQѹ>mk%߄WGAo_׫au{^]?k?'ߺj`ߺ:3}/׺ͥ/ևMAn OI+;dߺ:{a^\ß~D)os uzu1|ZMŏ~gNp;ЏϿu/~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~qsY'}>8æ H}yV7ۜ: `/`Ƕۇ[PX~86ֵ=:$kğO.x/M΂G>ּfKV_޾}Xߺ]?uu.mnZ'K;͏u>B>9q-ujD^OIS-PW\BPrU~^Rc $NZ/ߺ^׽u~{ߺ^'ُ?ߺc5(.EWӬfx@l8={_{^4KQA:d8 ok-oz:R9}xI<ϽTaN9>q}WS;[~8Ou=\ u~oͿ>={^~O?{׹76<Gxtͪk\XzT\'Lt_!7*~J4=lt%T.}xu2؂A[317< ß~U4떹so^ҎtĿŸ?O{U95eZOߺ%W\p.??=ꃭ׮)G,Bkm˯|j?\1 At~sC?fӫkƽu!D#Z?$[Mϕ%WU?Û[:N.?ޫש-=Pu'+]zg%܍'xҢhqs{N>_{5ded@r?uMu޶}Tczo^Hmž_~=P#qcU7{<:QDA}uDI%H771OQo^>&׺-dqyK?[߳\e Ndͱ$o7Tuu 3`q#߫Z7YH.O^+֏JT {Z=f~?O{=xt9B@(x8 {[PQr=I}ka{as{>]eVy>={"{Zìu:y,yӴ" c}k>׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~bYuK"ȿ:PCGM~W.Sך7?^ǏVbԂ#GN_oS`šI&o.9#YWK}9[[{5lx +=dhy8{^\Q${^%EO}֩dߺ]Y(Ay]]#a^:/~vG!E}u"?u ՟{{zp k{^Ld7EۏϮk^߫ר:rbV;C<Zw:$WϽylp뭏و<{^:?nobxk-_=N ~6~TׯV0H~>}0ԝDr-v'pmގzZZ/-,GޱՍkN \.>AqGuWR`iOPSJ79}Wst7?}OW:.n>+~H?ӛ~>zߞ?x$?׸\ Ξ==h(.n@Eԛ ƽ`u҅1ݾ]W9Kj$\1 _=ҵN|scbO7?XxzޢnM݉X#dw8 ŀ?5ױ#UZ1-B8_-`=W˥C\!ַ_v=TtQM<\~Ony׫)Óoכ_ߩ׵qH\ZH-&O~#W)A[}>:OY֖%o7^{Zu_}u[[ުzzl1 !a,/uju L"# qzE|AZ-tNp\؂p?_߇ _O"P+?<[s:` '9鎡Q]o=r?޿ :z'[W߿uqaM 8uBsҫ@M?WZbdX /GjְH/9X}G[Ͽu'c͹ïyjCϯ|$,jt Ҁ'=JG/?{@gdI$9$}ANx^ז [<Z"z mVQ{uꎦEZ@$s:S=90 ^X@[=KJ8_SߺIԔS9>ֺ(X}tW6.l n1KYM#ӫSsrC?Tu>嬚F%q6O[QX^?OOy.?Gk؟>ױ~Cp~,z{.) .䋒Oa֏4DB/?$UՇX[} z}e5lG?N?޸Y9un??_ߺYD=q*#?=|RGbOOt0EOkߺNEuߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uIJ( {LJ\Ozpy?S׺ձB@~n@}=垼8T -bOG[~@U͏~$uu&G2M<O^4U2[Mr Hc=.c߼]\?׳ 7?#l}uԺhmsshqSZz?O޺^m~,oX<u\ _fߑߺ4Y߽5i6:?߂=n=`/ar6Sh?x~=իڿF/pDzH5X}zM*/6'́7}>OuzHr߁ukpS5/ .Gzӟ#Ҙ _uJzu3;([~K.?_['=8)m\ߓ}<[y?ZXRW=s'UU9~ְ8ˮ7x?KA#U??=Ï[n>)ǭ˩cB%O{:HTxؓs"^{^\tA=T=g2b.mo~pj_Pcuk{Z,\87e6}SOo 5bEǿu:M*ޑb[؝GޅxGOWֿ {Z+p6OϪ}[6|zgөsnlcϽuMՇ\u<[Wu޻", ?AYS[Ċċ\<{zc鉀 nGПv%ۑ?Ә=poǿg޿OXOZǯ^7ߑ{FÃ?z$қIgCn=kv>u{7}o~^]Kp9{+=:%y??#)T=zī$?O~^]k'n {:GDQ'H?^-aɱx㎷:99?_RpoGP>]cUFumku:1OOކ:OR֒}?ַ,QO>׺{{^׺u>K>2K< [r>゙Ӵ)'6`~}={??.NI nG6uӬ^kx~Aց0>~^'Ժ@%poIծ:1qjXא@o6k [PPH?CO"s"ju "G,uqXN_~@?:}z} oP8e_ߩ׫Ԅfp/oȷ߸ةv Vmo~ mrA7=Ӝ47 ,O6r?}j:F~U=LUUPu{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u a6~׺]V`iZtEXq.Ԫ?gϬS41[\[n oÏM沢ߨ?!G6>_[XYŤb|6ܒb>>+)̧'ݺyS"-sEbB\sޱ^xo{܎'ߐxSϧ@D #byEQ+P r0lu so96~uuXXp4~[ո0?@?^Ǫ@zH}{xӮuȼn>G^5׿}{ׇ}}u@#)OY <}}uS=:T {ϤI'mro”<:$[A ~"}G{:\\:಩ yk9< }}:Zp$D,A7%OsG1Ҫ  }???2@?G{a?xeiPytE m\\XskN٘zT HXԁsA$c _cÈI'ןI'ӇV;e.̶s](ÇMJ?U~'6gfbn6Rn['DWV?.ÊGr׀8~űԑy[~5:̘ߣXoϽ5 D!,>~]e uпW/~{ߺ^׽tߑ43b{^}So,l[r?gӨv='~?u;!av#Q$X؂/ou ^XG?># ĕ<^Óӏ[mri[1$a#tťcQM-zqgׯWH7}LHO>ׇHsMf  .gd,O"6\uezɒokӔ:޸<_߿}zMi#z&$ u\r/sccoZzT)kvr/`Zu|zVoc6y<_~ꇬUUjf* : rUM=CQMŅ͈߫\l=Fm%u^=uijvasߺO0`„кƃ`]n>Aʯ?վGק5@-q~Jeߒ9[׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ\u~6/:y>ӂAovlI?_oϽ_>k 2yIOz5lSϨ9Â?}}G߿W׭u#O'ߏ[:t {6 {Z `׎nѡZ>}6ЀpVkg@{}F:O\YI*n06 _~t=L7>(5>8D7l-c}h #>`/AX҃_OVccpo\=cWiD!o{kߋxF^GL?3 9$BO>}N=zQ+,K<X Uҙ_$ȸ~E޳ձ1.k'ֲyz֣gLtjnZ r}zH lM@67Su"?Њp9o{lE؀?Aap7o$ QǬ] ǑSMMVS\ _O~H=G9c`en[VzxMm~@ p@Gq~}{ߺ^׽u~{ߺIiiو~o'o:@ai:O'ċ~yuNGGTW$xv@_ގMz\cŬILJZz *kҼ܀>}N8u~ۏ>=v ?O6mWol'㮵}o܎~{t:G'?V_^8Sá"@ svᎩtL/OV$VR6O~}ꧫR:M6V[ߋM%{Zzj*~ n\qױqR&@UP -ovU:sƒUp_H_߇^'+)#s{s{ \{T=c /.ߏ~î>*X!uSϩ}qQv{}gUx" yq!~Nzcs5 ,.yO~:g}4hFVUc`=ꠑOAҖY#H̥n{߅HZ4:U`aǽ^׽u~{ߺ^׽u~a_O~};ʁ\r㎮>Ġ7#ۏ{z+-w>׃M8:ʗ:'ןz^y\"đ{ k{k4F8\6IP>s}cb :x8t^߸%Źcsn?uGK>/_z[}y:7?<-u< { ^O#P7zg!pEo?OVX>}/}{:싏7x7ߺ cHu^Ka'M>תOIt?6`}uQ/~IUEbl=x׍=(k]_?^׫^TF!B܎SMޅ:iN h_$}]juMc{rֺ/7_OZ[QFFč#M1׀Q[6#(osWu m'ܞ>oOOٞh=SZZkO\2 ׏P#FWF6F<^@Asˮ'7{k_WeHmLAoMA{ T_X j4ܵu-k Oǿ bWtB/wԯ~{ߺ^׽u~{ߺH}5z'G{`uHD-Ͻj<NQ׭H_˭}.OsQoz|1q}Mbyµ<^8*Bi,:~GӬPpBCH?988n??#Ic5q{o~긯\4HBWCu?oz[RPȦWQ>{Nl0Ŏ [?_׭Tp=/:!qbInN%漈)&ЭƟ{C˭#\DVy_TD11d+?ٷǬ*4n/oQB{f'%LyQתҝ86^ ^z=zCw O׽pazNM*n$z^rusɿWދ1o1Q ŀ`}6s׺~\_x]fZ깵ɰ^Э; jXd{ tPGJ~{ߺ^׽u~{ߺ^׽tϒDrYgI@pXىZ>8ﳠk#R&VA{n5 u dkX.ڀ^/^^U2FZ "ߑ^@\t56݀'vv3#}j^ӟ ^tIC\2Roct+_v9 t'!K, &}zҺ T ,y|{^렪>׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u^܏u"=ux"ة[߼z{&e 1`zA|aT?>w'KsM؏~ u${(B~8o~}^奕wrn?ַO׃c=pZ)n5(\-}oWY_+^%bH \#`֪OS{Nĵ7O~Quا icZbnF-oϿu#~.^tPŀI`?Áo{m_ zj4XJ{I!zDI{rSϬ׺u{{^׺'ulj r4/mm͸6VQëINq oPq]zrLp KQ<IakߺR֎3޺KDGģ3^Yq^}*c/׋_U+]3U9cO?Xo<oϯ_>X~mשׯXm}{Q?^y_ށC%Um{h: }:(,Gxߦp׽u~{ߺ^׽u~{ߺHU.dp IPAՁݪ?7kkק5 ӧ]S6ҡ(?BHT,|OፁH{ ի{=Tx҂bʅO$ߺЩ8#= ;[ ] v{G[PRӱV2ǁ X>^>&^:*jE ߼z(iY \okm#ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uy {¨*@ߺ^  $r/u׽Sϯ{^֨o{^뎴5 -{/ /zB?~/o߼ֺjv ^8R ${^^T*c/aUtc{YEŏ/c5}lX9oqJG-?F<~ϭPW%$Z'ϦIF< `,Eny)#bk/}=:\u11G7+~][5:h ͏r'+_oZ:QQ.ם?_oݺ^lԂ_XR?V:R&[QOb9ZS=XS^]q o,矩ƞo4^<>ŀO=z=??zpiň:5&?W8XM8b HF"n&sdf8x^pM3$?ֿzzחSb{>}x#_=s,W Svq.)O:]P)zPxPyoϗM5?be ɰ_WAǫ8Gyc~?_˫``umkp܋U1׆i9Bn [mzYcPY~({sn.{^׺u{{^넚6rE{t3UP2΍(~o{zh?HYƶ?b>_:XW8ł,>X_?CjKNKWx^-=07I6{˭R=Lè*jbGPOSp^MsǦ KpǎA./mZ}<N.A6މzgLsn Gv" +oMOdٺ="i&G<ގzYi&0;7p8WNzXE "X^-Y%n^W6G?X=zInIkME:_GPߺ^<^ZqHeP_\S?CO׽Ri|.nA$_{7XuՇ$k?${[:؀U~{^7`bÐ?M?\xO?_vSLm`v=˫7X 9?/~׭_g^dc! OՇ :t7qǽ{5)T? _~9Ք @t_<$*xaakq?kӌ}8X)n9 ?M}oՎNW\O#IRy 4?Pg5mҟ Fu.s}8jdSdx_I`8פJ؂ƞM#?>N?W׬]AGPGsp[9?}x 8oQSqo?} pM?4$= %~*@9qwL4m[ۙ8tj ?./~,y{luF:m)7 a`y'oo޺i-zlNv9M:Ln:3< koGV^$z ?oNSz?No|Fz׵7ހt\p] {˭pEYHXؽ W_u-J!FR,VTp^׽u }H{^끖5 , о^I[O᎐ QLJP3J?"$ezV I:MW 4e$nH_\jD,PumbVM>]lS_92 P)o\kp_A]^G:V8PTiǭS!0ؐ*y?=Տ\MZGq0IooTz)x/}<^R8#-V)- ל= xv># ϧPǻq5飌t׽u~{ߺ^׽u~{ߺ^׽u~{ߺ]j׺?[O[ߺ\}D ~?u>Ͽuuon`Ǐ__ߺ]}{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^Bܥ=OHߩ׺{)y7?~n8ǽ=aU`>ڷ \Ņzש OA8!p}^ߋ_97&{L_.c[`z]_yּԚOTc>}+cZ8YZ-ŏ^saqXS}Wׯ-{k>iת|HLJ^- ׾G0?fT|c< ~,m][Ϧ<}#[WuZa{p@,9_?`1S֫X3Dxg? >NN O.">,>޺|I,?,l8[Ò/z9>_2KrMsm~9{=8G(m#]?t؏~_OGӮA_U cau-qczWJILRJZ}:MĎT(pTuۋtҴ8(FO8_.ȇP}Amt>_WU1YJG) Ӄv=HM>*$ux:GJ1?QO^IY2۝Vc{뱎?^aEL-w ~N ě=nu`5\j#b>1íu8=uYR%fcR߱׳ۊ p@Ut=@}Ж6:l~.?=?#Gu: n/?ohu L[_CrG%X-ɺ_+Ձ_N)'k0@mMU<8SY$HUj]D/uǠbYbKro[yO~5׺5 =8Xm~?HG| p8ax}:iI-ݹ[sjA-m,kxb tVU6AK>Ǥ>#OٹL H`,>}jz }׮`,,?z^\ Q _o>(N7 ޏ˯bWR{~?OA育t!tT; {ѩ֋'ȶqj9G'u rEjzuӴ[vcQn-w STӧi'M[,>{]qlAGu.ei# [X7?UI鎧{Q¤ݛI$M߫N4='796Z9x_,T@׫N=w6[~Y9$Zmsk~W$1fZ~.:[ YP2!@}~TyGWCF2ݺ$k{^׺u{{^׺u{{^y$~CfꉵOz^PCX?:m| o${^nX}u$cou)6?{ZRG{_zߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^{^a~nKi֏Yۅczǭ}Ks`~k{P yu[Z^WX}o u~~í7QbO/o}k#~mq`E<Ï^y=q"tֹ[MGqu:/唁?ӟ˭PNOAz}v盓~, [߳^zs&=SϬЋȄ~vߺzw1'>Hը\{oP&_{>}ApX>ںm5!F*؞߿\uN=(@l-]Tt=L)ak.Ho\q=ꞹ cM(X'_^.5?p[{zz1SE X8Gu>Rң5n _>*BB}?@6^~ju'J#h>^Lg5T kȣ ѭ>NJ9?Tuz,S1X}$[{GTק/{{ߺ^׽u~{ߺLyN "ZH: A!ܰH$}܋>^cny?<\k_{]x[^,,9~߇^.ossO{Bf-pEWl?CqSM%??N xx>|+u#{zs׾[ӑ_.垽6#lxO_~zzǏ5/ut%Ą~# ̀ qmN3*ScF&Ðփw%+rGΜ[o{~-q r~~Tzhfn,l>knTvbmk{- z`* r@ҧ>irj: I%mXcl3Yq.*#}GTuZSY^)RwtA{qՇzpoT#[ .޲q׉HQ2! YTm}<ҟhHeEZQ~?U'SBBB=q+KZv<5ǽSW1Ӏ5FK{3o{[RDQE= _no{5w{{^6ԍ+uِ%bcnG!v#^Ɵ1Q>y2y/v>L$ȰSn}L L 9< }?jgNK$0 UXGۇӦjz"4W~}uߺ^׽u'~qu>=uܨO_~]a2O\O47~bS*)f'UߺYVZSn o'ߺiԉ$'#SG&bF.@E=z>B-nJ?u.naom#ߺѥ:ߺR#{ZS?}u)s~?}uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~tHR׺R_ߺ\ LYm˭yӮn.?xuY }D#m}հOV렯n<x_~{C}$ro\?>CN玜(v ه?[߇Z>KןM?m3rc-]lNI=#qqt=xQJy`[޺}:MNb,[6n,Z=f# 7 EH% ܿ,>[:8똠 >ֺ(Kɿz>_N%Kxyy˫/~7p-aoױ^K͈~8yϥ@z~޺OYR)Hy}^qB) RرubIoTDtbf=${>QP:Gaq}8#^㞻b9c$<O~:Я4U.b>7O~^B=] k {ppǦ)Jca*/kXX?_߉?ɨ;THMDNI7}qztҗy.t_{è5G)*H*_~t^RdK.իH0?Wn?߁>}hӷ{{^׺u{{^-[EeO%B8{Tew,CX.kNG׏z8=#Z5['߸una؂ZZqA6o=n"* B{z,aZX oj:-m%Ƌv}GWYLWұ<[݀|:o'/tdv1rEGi}/ZgN:J(Ḿ z%[iԇ"p~߼שzfoV'pO.dF:`pE ؋[{{iǭPǩ1J؝* =z;vRԫD7%AKv4`VYF!T}؏{Tn=(!E&SRa.ɐ >'k~?Q|kqQS3 n. :E'\u=W(i%r]G"./{izkP.urߏП׫# 7Nb9*[XZ={H(pSY"I9b$(q>:t%PEvBj#M?ӟSj,5i-oGFT )P@{?4F}1.l>[^:77_]jJ}?=yu:?Ͻu^mZ=Lo׸Wzߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ\tl=uߺ^ZV?Sas@z֨=:U6nE6?_z]^׺}HQî>D?GC!#ߎ8_ּ^~_uv. fH-~sz˨Em=ꧭzūom*ÞOp}婢kǨ濎\!{˫S5I*Im:u{{^׺uێn p࣋𥎤^yfXVXny {:)@r?oZW=q[qf9IQ }ZqpSWB2(7pX}OM9=Z@쩨l@cRצ%eC#z8zV:qE"<ߕ?O~֪2OSWTFn@k6>C֋ ctyc:XSK^Hkzo?3_W˥#kj' #Zܛz4YVg2ح<#߱?u*{ZoxWI*Ek {k'Y/KSkj85(^PF ioqiNG1P$,U"G6#}u(}:)vc`?oQGZw ȎM׀X? փiUOXp,Nm{ 0iרtٺ/QӟǿdW=Ai#ĒYnQ:#M*BT ?ft3`c+|~77HSzm׽u~{ߺ^׽u~2f O. xIvoǽ=̼Z.I <؏_׽yߧO߸׮_ :],y ?WY@6c{{GU<3で#Kj,J? ҽPөZSƖe [3-E]\8V{I?F:^SitȠe!7n?۟>cjfKy7 h[4XHjcTOlX 0i,k,?O.[]z?j (8)?3׋TǧNi%tөkUS#s^I3ӄ401k#JE ="2t?u=9u\iߺ2||fWʿbI[?Վ}S!0\^i \7N]K}&:M) ^஝gމzQLrd2L@mZr>[`+QiC ֿo~\I$'>玼:4}-jteG6 nmϿu;jz6,\̪- PD P& [X0u^'=&*8f"ᗋXQL]Cpx=z[~~]M{ߺjs#ňF?֫']{ϩiּkߺD__{SvV۸ĢvmT6\5EαRe&[$ehxv{ຼgi]"G\` -?o'S+ GԎwoQ&VeG_E {Ǿ_9t9Z-0Hqeɿ_e=ooȿn~aɧ&skE}ru7?0OidϹ5uߏ|{E7^s4M?^7_.pѽ 7^s4M?^7~ 9{M_<{2d~ Ͻ|{An&i?~A2o??}r/駴ieɾ޿7n~aɧ%skE}r/駴ieɿ1~?~9{M?4vLJuh\w}G8?Շss 4iv7^6_}[ٝk}eAK:ymz:MՒ,Yu0=ͻEM N Js-7&vʏ(m̀iar9)\gz ~xr}u G>ߺ\du [ߺPom#G^XM|@n>q~}^5tl 7M}{N+Ww$VߺןXddE H/o+ZCy8zuJȱjM&J_u5U:B\>?^T $$\>z5WǮ6 ]ymgW ׾WJl*I! ~luiҋ^[>6|9`6lVXԻgg`(MT4ljx i829M#WR'UzjI3F|$>_wi>]SaU˝Q؝7iyfg؝E-[ _,NC$r::7 pZthREUz1l] sTի҈EO$ qp-[߆8uq½6SmPH?P^u2-̼,.zMzVsSZ¼z6'6:?~jcJ| UF~\..@[oZ|Qc `@?ߺP.jI>  >^آޢ90$sp^㎲FԷXu[~$p:57oU<~9?Ow: uk[]_r6^׾MzW:NhUH.ش<_6~:``O~]{cW,pZSpU"?&,Ǐo=;E$@Ak_O߁'WNtWe.5^1j࠶sa׾ޡUN=e܋~y׼:cSplmk/:&[LSP_Sk?~\cWMU{Ƃad?7zc>}'*ovpm@Q߾έO˾k%N#*Pۓץ~_D"졁kl?qD|܋p䖰#ȱ=ιĩOR5O O?cℐ1ӊ8I% u?}㇟W_sz]n}m׸o\ߩ׸~ ZuI"X[^GOJuO"r4,E޼b R@,o0?<:~h `y<pWX3 bI[޳rK-?n9GsScae7ϽuEj_x~ǩ,͹"s ޫֺr?^GJ/?OzIᄒֺߺT?ҿ*??6G̿?㧡?ֈ5?{ao{鮺8GuƏuƾl-k[ߟ9ߺ]Kr?[޽u?Z~lADc'>޷Oۯv,?ɿ󏔿Yյc|<K;H",׺ߥߺ^OпUz jy-n@ܵߺ/?K__>Luˋ Y׏^<sׇN"xl>jz^xm8 Ϳ\T@_ЏG׏ˮ~.~}?>ׅ:wo͏OWΝCn,JOSǫko'ʋ^s{n,?؟ytEǕk\[|}=uZu(O׀(yk{[='G* u7?6iZuPRuśw8ށ~εNOia`-7:yt&hsӍsm_zT [HUv#~_nEMʂ'QsoxJz_.c5O^8GďO$qd4)^UAޠXs իrm5*@I+sŬ-휥8uCϿu{3mu'_ o>KmK E$>[%..}`oX:^UK܄_7_z=:r4БuK:H:X/Uakz.L`2 zH&P qִǧ:D13\ގzˠqճS@$6mnWO }]9q?Ϩ-7o耓}"'u8={H 2JDg:Un@x#߉>XtB[ژbC.O{[n#=@6 >P,9?C`y@MNÓ ~5=J5r*oV_зPt8ۏI|sׇߟ-{\lOO{>fJk؋?_<{s֫цA<\y'Λ8I-!14gHr.uI_ޫՇ | O{x}{?{^^KA?:uٵ؏߂?R:ҟou\f?+Ϳ'ގHV& tR9@{ǩ׺u{{^׺u{zZ> }.7#⑚-L!ֻvˎ9j€%y?vzקGGM!TjENwe?C}gxZ/>ӝL@2":NY{^3k^GAD5,~!s۪u},UP>ֹI$#D%}BnyБu`隣sda6 ?O{z}:fzFf3(? Ͽc[XHv%Yߋ޸JM99{={,L2I2kq#{iáNrTN)p5q[ߨj xJ] /ŁֆsykeYiIx>Ǻ<8tl?__z'>O~Vn/δzx? [2d#?Oo~fX.4l ן{#siO A~ ꀑHTutD*$ zWFy :Tx<s/> L/?޺z{ĢۍnA^zyL||^RҖ>nyjHX>׺ߺ^?~=ߺT?ҿ*??6I̿?㧡?ֈr;X}y}:HgL~'p}?[u{}>}{,Ͽu׏u_~z_O~t>{?u?Z_{htM7n߹_8KU[^u:b.ν{m_GUvOmO~ڛ^EᚨK4siii) ]z26r#]G=Q ej~?w?J=zMm?Ϭs„wɣs~?O[G.l_{q8ݵ?e<?޾Xܟ}uo}wAcrݵX:(G{Ěƽˏ]ׯ}Y Ak_f]?>O@G׾{r?nǎI?׾ ׏({~~7m_ߟ~A]{u; ܿ(F_n ~_{ #ErhMV8}OMr>5^Z݇ wi6uaުSc3?s'D9T:tͽ ` CϧzОyuV_40 :5{N{-͹!ڛSn%)k䡛'Ye|S4tIXi$AK6e.5 m;7mQq=TG?ڟo}_?͆=W[S[ll?$PqjA Ȯ i%1kӰxuu4bvO&N5u "},\x6^T1Џ]:_d>^ w&tap}uu،u ,S<id $QP+:f gݱ.J[Z>z{ | ܳ^ g^ZivN.{kmcs[zّ||)%2+UV6PAm"} .fY?! n>2mr8~AOO_{5mO9nBjd[G.m?Ϯ!mZׯ}Y>e|_2;!ۓkuL,.ۡjq(Q]X4,(/Ƃ53yڏER۝S?STv.$\ܟX>*>Ηي[HZroizUgYiA%ޖ6o-]9^[{7z=tySoP[Ʒ]l5kF:D)e̎,X:UZڂ'="k1ZT| OO7_p?˪}gG\oF7oi_~ez~6n/O{΀(6_Uʏ?GuxNP?kڋ{ލ"м#>go2o˾e~_?~V?Ϯ`OeEF?i׿w˾ P=utmh~iuԓdnmonl6_WQ/'4P3: FцRSM%RiJqXVׁ?H[9!smUqx݇;-JzpMjႢY*'0ƪ#Bb*kJti"(+^=.s]:7SԷMiɼvP_߾^ޟc1V,?=u>k׿7y_֚|=b|;ʞT]שּׂUvEĿ A,-kc7T^4)M^ mQ#`?{>O_vmCO}Tb{3)IJZ|>ab-r!b ӧa FZW7{KPzY{o;3=UNb毥Ujʸ14/VhUJ#B(-v Ej`Zw:j篺Z֐'%N=BwZU@By~ސ}i_Ӟ;o:d6Ym<{&_X'DAjT 6q1?Ϯ_iڟ02n־iڟ0rn{>N9{ҏ]{[{4KA߾^=rOP'Fhc,цlZ޾[l;ȮU؜G`m|XBWbf!Xa P(k\:VC>BIS[jyZYA H9 Amo~kNO.>ٛ-ܛwk*1 *)DM ey^F+sjڪ 4HKi>b~rN( ϻ Vx,~k*Wo􃆮aMr?~ǔX&e[6{6/օQ{'CvѾ2I"޾}VcN>:|?mB7=8Aeտr@՘c?=_XCɿB;U^6'cW=}瓕dm4J}`}j~=xuq|G; ;06'tl|3qaҿ*h2X"A$2G6Keu>ؖ#Ҹ'ԚP'_';wq{6 ,I.[-CR5l #4LcrGk).1rH#Mg4ꔿ7NP~}(]"?o獼zmy_A.Oz{A"z2[wPsRI?Ah io:BJ|jڌOokXcoHS%K |q%[嘋 ~AZֺzBX3E<>)oSZ?[mqZSW^?oPwp? {l?]oYxJn $kȱ;2}$_׾~:?qKYI;o訫?JCU,H_TLPؕ6#GRt7B 1>q}ަ߽hk0]y['C^ E!CcΑQj:M;ϗT7(Gw̔6TY QؙIjY0ǍEg `̪7}A_ˤVk骣ߵBܹ'{QؿϷv+ek&ܱנzQb?ާɯj(6 7P&3mcod,?{^/yu-?B8_H c~A׾u;WF_՟}kBD,}I apF͍`J_J?u?m_uX(pc=]޻{oV梏pd7^ô;Y^LѽaUA] Wb>ҡ )t]*0L~^=gMY;mn&'i~gn9;9|*h̐iZqͬЎ=Yz{^ߺT?ҿ*??6I̿?㧡?ֈmm-W}$O?=p$ DI<?7{ME={m՝qH'ٻ')I:h7H7F#sTgik)D8#n\yqc8l$dE U7McؐIكǚ8'{ɲ& @Ҥ|~L.oRZ](\3 Ǵ3ؚ wwm\˹Zݸ0E#~JX@lZޤ0ӏѻg1'ټ6"ZhqR_ySu9_K~>+h Mϫ˼<oYiM"d EiQSW'4ݽo~/Vl?_mz3$:u OM8o岾[itoZ=^ȽuZ=uq{}׏Wo?{u>׺ob>ֺ|J]?:=^~[.}Io>)饒B8-{}3]"[UÃj7?gIn`>Kok/|~_{9 O7y˧[GB?euJx$mp En9z[$d*O9i,~oo['۽7׿}=u޺z?{{׺u_~^b);nЙ ߓc؛~in'5?_I׽ m FT|b k_Hs6kë=|;@cm|AW~ސ?On׿׿cߺ^m]{~~Ͽu^ 6ӷ5Ev2+CE4aD-_MKҮ'I8v?O:}3G٪#6Hn׿?ߺ^D?{^Oo]{}V~]{s"˟ȃel~O5c'ho>%cb FqttvS{G>)ҢGZ„?r|{Ӭ?Z'uowߺas_޺^?<-s}{}޽uxuꁪnpO7aSU>ۛl[`iyVH{+WtqZm ?3`<=EQWbtcXIh'aC?>z&]{^{^>׺[}u=uu׺?VN>>JSn jud }~{,2ѭ"c p1ԫ?@Wl9#mV:zurTղ6@#ǧ}h)ݷLvjrD.w{-ujM]{ǟ׺}~?u<Ccx޺[#?E3%cc/}>U숻:{zo[hm* ES%zGPu<:rEkMsMDOnrMN}>܋EzfoA=tU׏?ǽu_׭?~{^'oq{~x??}o/l,d UWVW dSVAe$0IcBU*K%f$tg g>8}lO-FW'q0n8g$36sLƁ:ף׺ߺT?ҿ*??6G̿?㧡?ֈrh_ u7;7z7Ͽj=]fJZN#M0%Qѓ@I t9܏q>~\+辧tTmo> H/Q{/=1xLEgGK03yn2?ϙo&ӉtuNRral5F߅6IACS %+j$i"E1y[pV=cˏֻI59fj!$W(@G\~`.Yj5_f1'kTכ6_ɏ{BMЕU :AೳrmR! !FJRM%?%IR1כ~{\vs˜[I+H2tk̵V5B#Q}9ǔ^no1Dfu:I=Y[ |X_,}-Nof8zpT^9UX"Dr/d*}켱j"4/>u,8 UkGQU:o4MƬi$ J|xu#!7mk#.w4q|R:'7UV@O<8ONd$:~\Ӷ;6ȻaYJvB\õ]%3AaO5u_a3? ?~ mKIixen94͹)Wrxڹ3E4cTɡj+b.#"w'yvH $v"F=2V9 sB}YCNVv-(2Ҥ%O{b}϶VunPLW~GO5"TgJe/[W9?"N~em{ ?*<ކxu.iKzdGRC1%E:<Q_cgikeߥ խ{h*Xڙu6T*I}L*k|+Ϝ]mJwďby: ou/ɗ6 ѫ٢ю㨱ڼm;E뎾\|3KONm䯸2 ßAY); V,è}c$|˽MOt߉~/v6;]M̆ܣO.P}ێ(`FhHjU5pC!7Wݳ{^A^XƓ>N>Fo<pMܤ~5î_H>TtGa_0#1>C+œukFd!QQ TC$SJ`WVH۞eg>Oոl5YNk4:-H5F~?.bHl>Cmp3-eĽL u&mJ|26'INQ݋&E=;_vy*pE*NI9'' 2׺Om;4Qm[}jGä_~>}ճzGe1#g^t(9ڑL+ f[E,z!v:O׿{Ol}R#?{;til d9TcMtTuˑXYeZOu(ز+ ~#F=5TUJb@U_ߏz=ldW_,9MQ1>!~ΊoD?۽5դJUөhm'_O=c(7 y꼕OJCWY*8$DBň]_/0CJW<_C ?y>K_CGQ*'|[@>)عS|PO` =q\}M(>n ӭ7&}|3? [Nd(lL4:lQHLV RR1,- =>??k=^Iv\q{=O'[}U5s60=V7^俐g{plI-/{&=xZ?ilETzн+ub^OUVs,>#߾=^'!׿{Os[ m 7Z)D;*}mCtݛq)JQAJqn*"43UWIPtKJԞK$zYh[ZyAՏ˪fˏ'zF[i>ofYjLMSOPUr$j!­F:bh|JiO'cx8"}Iu!? [ݝJ?~RJ=oQ.[zo]{qTm;\e걹82G9c[c#nqu^Or/*zC_ƝoX~}__t׾>?ӻmX7`.~bV={_Tͽ6Obo ײsMNٍ{_,r#K4Rbr\X{TR}##K>F&?uT[Ƕ ]{kdڞ zL*yR,8Az{@% CO>B>۟U9O}z ?z~S}ޝM{k}1{.[EAB+$Jv<=z@&*1R:Wȁ ]?u7R/CE2nvBRz7Jۇ7ۛ#YPTsuXXrQE,sGQER G gz9QFcm'M4 m?u׋^31cc/}~{G_Co:>7]anw>߀u֣̺'E>'glNuyq{gYHqԕ9:VI BMϺR˫*`h'~VĘ-RoW!{wQu}[tQWQa)D%1#ÐʒzZRz }?mOIOSM hI4cI_~_&#|b]8ڪ :tnʩ(gYzjC6%5qݥiNG"\$zʨ8ZyPh}R,##xsg(!'39LDScXkO$m.,xC"%84=yW4?)ݠd\4G&čm #ӣC5@$qo3_`I/M M?/3fgZ iWO;{$oYf ҈azjPQ$I3߉^.V6!CdJ  X_auCKz7߿z=_CG^}{PysKz?~o/T}#C v!Q=KEK'xO4qhzxðE2(?MCT#zPֽ)gh:mS`5[ SjDY_zhٖ!-DD) ,|XBt5{V׺uߺT?ҿ*??6G̿?㧡?֏i{{'evсw&XErիNr DE Ϯdrfma>lh8@=}FaE1nm9&z64P<`7~A_/B}߸̔ͽ?949Z*Cao?}Òuq $ZµʮV).xyn]I4,w8Q4~gh_'g<ɥνdv;ÑRSNԫ`EVNj:a)!-Q?Zon'ɏ]c'p΋̴MMLʼ)֣[[WK `Mt&QW΂ Ou4[%3-Sxٴ[SexjGB7j:Db:4 .9[BMTgn-׾i[dd`;M6&* lr< P$Ө${~[|-Vzm푎}I=wӝWBu*+>ڧȋ-:#\9[gQto|4#6~9)?tD]'s^NT5ԧ'PU*BOV߿}7F[[ubSlk"ڴtzAۣpcUIJjZ"#{7ȏ;6gꍩf\]n.CMSQC$r,3JCHuuv%şg>|ڴuMM4w椉qoB6RDl,GdG޻Wn)& i *Z4~~1wm ]>,KIcM?o߹NRfhEVר|<K;H",׺OoSN?_jmKuC οĞ/NA!h; H?GnkŮ"?Ӗn:2D_V<Lu87k?RO/sdCUл뤾7Έzod&GC~OA [nnO}[g^< z?wR[cǦ~}u:ώx/eO񟷣h:ߙ$ Z1ӝ_NyCs}"~F?Uk񟳤zG{]+_a}>}sMuhőֿ~0醠y#zAz:OO-s^鮷&LC; bi^Z:4_˫py矯״IĖurB{ҤuӦ&,W&MT_ ֳSIl 7~]kWƋ5Y 5V#G7^ރ?ukC쩻kLA?}=CҫQ~򎶾̵VNF=t=HIZztϐ8?^|;<J;_?<GD-$_v]oEG(Kqndac?Ae2vgT@AٓJy } u}z.BDOGo͇>5N ~^2- 5ޮI} [s?-H꽆Uƪr/}ikz*̵7m9 Rz\z{j_yohn!tvܷj9nQM<^,_.OPE X_7p=PW_Ny篟gkq=>_ O{6_tF)n?'JtAuOVoʼ^M[Ymk3 ZYk멦_,eK4> ݝP+D ÇFƏd$O݌&87QJ>G_un]X߂2}}+c$~/p_vқe??_?Vԥ=JOmESwKZoG^ozFq~|]8yMOw =kl-qJ}Zׁ~#|ߙwW؝W>jvM[풲*z#)<328ᕔ2fUkѷ4iAX}ӧ:{{^׺d_y V:IwJ>][G_{'b#ڎ.?WUy$츒3ђWA nJJ'mkqTt7#S?ͭt;'W >}vDoլMoۑ$5=|G{{4藥_hu_D-:2mwAp}<}gPWv+_ˢ쭡d*A?ێgj݌W|?w`?le1]C}1gz8 7uNlH ߗzBf ۗȿQ8!XH䟫Zo{+\>dLR/;Z_ROξ{ٯE=9M8?)HxuZ/߲ ǬQoJ3[6{%,#To,A mso,_Zz7ЕNޮϹ*I?>Η?~ރ{Ut>;"pX`8rKeRcY6ܣzE ]LIQϺq|}aOGNw?>܋EzfoAk٧E])No=E0n|?')HSlJ])V 5u{{^ߺT?*??6G̿^?㧡?֬q#U|TTQ].orv`} pdjTͬ=3[[{ySح{Sl-"2@ 6 "Ǯ}ȱ I7^Ivfls;z|Ya1Xz~ovo|Vqel#dyfTJ#[4Dd6>R >ݽ>{uƢٝ]۽: hC}V7Q0>JQa58c}׺agkk2OSc].7wRaؽɹwFONi@$F 6Fe]q#/CO_ˮs;0a^gRdvߟ/G.K]MU #i4}.1g5R^P$[oiLf7}j$[*kHoM_XMs8=Ǹ7;\˳9Э:5S%W<uN Lk'{>SY𧷻.)[szC+;E2dxmνҺ AE]׺ѣeTtش٧s+fXu^X~^R}F h*8Fql%&mi`5O%XzÜҿG'?2K'eڈ?\kQќT$Xۯv,?ɿ_oYD_ױQg^?/$ӆ6[+MInwϵ M`GvC` }{,:4>(?&K'v۰QS#։?Kq{5觡w=<m~άG_D:|aeIm7pc/p/Ï^#|wo#]-=ytKw7}k>-th?~Ѻf)?C5>6oi?}"?WGm^Z|gzG)~'co?tnS_LB֌q}э=:`4p!}>պr| }CWעUuGb5o~6<\|G^_Qւ Wy<s7NW,H/ţU?^9y7=_noߏoJu:kY{M'Ofr85{BN!Vk?kCm/oǴ_zUi񟳭m^A(>>ъ='U\ ըSRlV8Hϯ'hq}ݠ[:_tDzC>z߇2u*7 <PU +SѼGz:KjfNѦ[ usl~tϧ Tuae /r?{=x=MzO1X; 5=w1oLͿڎu''G!FC.O[TRGP=Faq}XXF>Á~ߺs-ݱM9*m~tN= .7-qbI?ix=I׽u~Pj/Dᣐ?gInEސn_i=-훏Oj>η3@da{~=pu$o~+ըH]؏{ۋzuP`tW>mW?J7gk}A17zߞ?qg/Jc?߶uSíЛvHD!R]6R@}W7h~&Î/???FTnv~{c4?q%ut)|I9?ׁo`u_(svwoo)?oOϻNUv_۞vaG F->'պ4%WFt3te2LryӶtxc^ P8P"CX&ߟίK{hW=ۗ?7~?ޚ>_?i/ͿzݣvgRUuᒢHZHdc3{uӀz, [jOaU*mT?S'鿦G}'g쎙2`\g\N[;x :Y$8b@F ҀAo>5AE~7Үq=ֹW=U?ܔu{: CwZ2o~лp9_o[?>R ?Z^o2诣~j|f1ۻLo!~޷VpĀڬ{+Ҹ۫x/)%. ?GEvnAցso.zq?xz~RGhnO_쏡֝O *ן[C.ٶ?x:~ރ>z݋wR"|v].%7oerYSz׻U{n]bC(n_/3dgZO>*ߟϽ.~_]{_]ozwuſq]{GƏ1j[7H^w{^돫>ߺT?ҿ*??6G̿^?㧡?֤"FQ}'Bj*33QaL/4mP%]9b=?8ystڇ+S]m2fߘ'MD=~m+QSz-E \gYQˆi~ CLV+ 4GԱ?}Li˿hm>sM5RmVɎgbzlUm5tUhR{ɻ#kLUm~9GrW㶾[#͋J'tSZ{uhS۝W7&]r[ƒ wkI&N\GRD`Β(#ߺW00B} <33gfdTT@J l"T=uT}R|om_{7vI;gm^=0gQēK#Қ H2<gd_~Y//~;9 7q|epʍ$,n&iy R{t~m65jm?:Q)Y1jVⲖydXjsn=uIAO!>i|]7af7bPu 4꟰$-5TM,T, iHH+<d{!ۃ?dqR#}m_d3.I#MqH\G>Ka۠yUFVwҪI 6 7;g'FXTLC6M?_d`: 3MXBy%ErRȼ?Oן|Tnen]K#5"#Y뼟v@<핫\nl2XʅC ˞Uۯv,?ɿ_|??,"N؋{^ꌿQ=ш$[ml6:KwC_ N9+}?=f#ҋzx/n9?O'oџe %GS?r Wao?[ٯE= }t%޶ʋuoY>!Uk|8'wCkpOʋ@{鎾rymK>ޛ?Ou얾;ۃzr(xk:ɹb9otӆꄿͿIO_nѪZg%>޵P{]VN}x_?˥p GDz8 rKܗ~׫'ɮhf8"٬?/L1J}o>鮷.Lv, \3 ޏ4_WD )p3 ='(? _}?u6̨ʞ 7m_)5=kC jlK0ݚA nTGP7Ho JtΑu|*N?r6[}*mU5se,~}F#I[T}ߋ}=xrgv0~nf=!?_u~hekc1c}z51}:6P/OaPkCqs<)P>.iT蟯 i<amm%Tm~̢iq?߳/W_uze Sʯ#z~/KzN6eZޣ~x'ުO>AWCg9s&hwv{;P}z~k'.J ^ᬥG8z1يGWQQQ Ǐoo'.R P?=D=If֫O_Q^YOgaKJ}ap-cQ_M7o ֝r.]HGgu#W߅Gׇ`bl7jg[)=aG}$tJ{/nۏ7~:!6?H&硬beh\jSfVV)uqxtˣ#iqB: _{Ug_`vXT]|j~]/["Gm8xic^}s׽u~Pj?LCګ_%G֥7oH:Jڍk6`/훏J-mj4 4&?Z8ףU>ރ}ڌu>o"uc;>":%һ\O5 =lq}7$y(ZGЖyNqWXwKw&f貔Ðhd#x)^XqUt&G6EXr~~ IOymmk};$E.L$ Nsvr+@YHՂM2h8|3 AXȬo qH}@uև%Ge~V0s>tG!FOz9Tu_򲈟tn[ّPW~?ejz4!ҫۓWLHa p4m`>7cퟷ!4,[Qc*, ?V'YᄒAֵ*G{Weݵr8`#}}۪z'%ӆFe:{++Ѭ%G(0bcEzI[K2GI uYgve>y ?ݱǯS˪ljv12]o=ރaCtZsǢ[-=~%;Ν*o)XDS~etWϢ9~ϽJi(o6Ki'Ǧ?QmZ~?f凷l_d%?lq _[qImo_dYBé8o~:Zv^jm6G}3}~~G[. ymָbmŹ?=)+q>d8Wr%BXZ ՉAnniy.[kcՑI5C%DOXH$ipޝQRPJanG ]?iuS$nQji֯%$ZbGM_z.oOfO>{vX߾/_聯 `TmQO/{Ϟ^z率{Ϟ_}a[}D^˯}ċV28X@Ğ Bj)8ڹKzXpPՁ*@P Lˆ{fء𿻩bS`7-;q|5tW:OWMKP Fos'{?Qo\^*."e z)ڷ) w0A5>]'{b{Kq|s] {G`>kn2)dۛ_5 KIO 榑#S]Q,hia56Pxf}dy~F@t5Ďi E|қ vܤ o i.?_.j"u@Ae>I|?fg]}=1SW܆iWKt-zouod5Z2, h Av[r\B/VS`1Goy›€m_~Aglfa fǽ)*:,udA'-`u&.C{9y5,^KQEM;h<'H6 P)7Uv̍;|+Kv͙Y~ 14NU5aX; lq9FYTe \)J9/o}}+H;D>$50PI:4qכkχݫ0WF6Y&WQ1a'\Vf-4ZdS䍏;,;(oxH"j$2ueA_C:mq PWT{aBow6Lhq1oYڕuz鿈ɷq5=Aβ2ԫ1{~Y.0qzpcV.ĜN:jh\Gm|?2hO˪\mw7eR/P9n>r ͣɸ2{O+T$Q0XX㎜}=}9mfeN:rowq>EgaR S>vka#i%) _  MjjXIu$$;y$w$1$&I>R(>ϐ뭱@ <[i׻ẇ/Vl?_mz3$:u~(ӇO6:KuzC~uf`v uKٸ]=nT~}n!x}Jލ1Ǣ;ʡ~ fFu$p?(ېj>ޚ~: o^~?/Kmo վՒ:#54Ibopm׋BO>=mUn0ycQT(iq9./kj\{7?~GQz?}E35Cپ6{Jti€i=-puvhUZg-oZ䖡z~6틏OkzA׺З ?:OJy}0C_ZO}n c>-0ɵcsSэTm$ & ~>GX~.@btoskY Ct\jֱȫ7#F-[#.R/?ھu|?*NyMoJ>3unQ"QoS}xtt&b~tF${kDcOv-ٝ'}n r:$o[-n=ۭy/>5tN=߳6 lgU}{iN>f!.,M`+jP W=$,aI=2fE?~Lw*\IO76Gn:Icu$l~o!0p 6{:ø?~{Ѷucu5u8ί!]C6-ʬF[+ȦgaXizB̬T=ȲK8S[)ot[R{ͺQh!OBMR OԪʏȷ`/7M[[m{>/I7saoN롣jLnr!o^>\UypZ7+= >kU'#Xlmx#;wEKU 7N؟/Q޲koFS?zt.*wT0Z1XG|iW68/O-?N}LG~#?v~Y?׾_ֹ?̳: a>Z=bSmkq9/L3=lp$AيӦĮF=-}ìjN߷g?i`|oUs:_fiuﴝ*׺u{M]E?{Uk񟳤zԧcoǵ a##}d80Ǵ?[j>;Bxnp$tl:K`J?%U {c-KxR=za+$)B>ǽW֫Wp:f7u.!?GwAzjb L>]h_ٯD?ml>sXq9FNaUi!T1:A` ~9uǭɣl± *&fvZyZ5f_{KH][a:$n?!X >Bߟ߅()MT7D}<3k\V )}}hc_5{4NggIx:~|myn?ˊ8ŸMn.u$ <W0Ѵ64C^_m_y'obryJPUe2lT4Xn:*Ej#M^qU8ISQK'$NRoS;!J+ 2>%c>/_z輪0#׿?oٿT?c1׾_zֺ _ ҿbM_oOFdm^׽uߺT?ҿ*;.I̿?㧡?֊̾_o)3 O f_ ]Uc{SE]D,nѕ}mͥ_ē*xUtu? л::dd7כǼ`7KS$AR2Jf%u-t7ӗ5t&(T Z" u_}۽'Qbq5k (UN:ɮuoICQe *dYKKIH#X؍"þQ^DW[;+)*dAj֧+_&<[ۻx0"Lrq= 'Mo6㢎}7cMjhR5Gn*I}(4(jx ޾>oWﹾsHIv{Ej8dHrNk;}Aܵ-sO#x@kO/.Ͷvn:=SͶ6U&#`a%acEQKhG>y[hy>n[F )夻7y4@ۖ}KͣgJir@ ySG-҆L?Rms ]~Km HOq{o$6QAp4rybrAT:3GgQtb$OF1ny}k}4ɘWk #SzxCO6žrf7f ?O>!~ΊՇϢQ:ks+m 6Ӳ9m5ˮGW |Xd#؟/><:xSQP+]by?[GVW_QS1BR>7Xq~ԫTquWƋsvXq]N?UYnqZֻx_'p3o{|Ku6ړ$e( _OeAQJz7pXzmab.?SG`|ըW?Wzv̮ ~~ސ܏^q{xs]xߺ^^^׺qߺzڋm#/?jr?ח#=.~I͏ЅۋJ(8tp4郟?_'{&϶-ݙݷwg,ێ1׺3Ȗ"{G*~=4eWK>t{^׺ug5u&DG!U~Β]RoHz䖥x@7`n(qʝ-TYbB8J}~iѥGY*]mC{ ԓtJ8*'  /e=PzuZY˛TW%|{A~_ɕI51C yoȇ驉u4觯o׿qo>ֺٿ3a}l2cIoE+*?"ah*Z͇JwFGD {O1eB}MDf:q(hz m6>:pݪ:]{׺}Vd"8:CGI?Z_c?^7>0^ݿ[?%;ΜUzpԼ4H4%!5qo쪸=z ?-%|w;{Qjuyoog=(}܍Vxuǭ11*ܠ^<E$@OQ`+oZ_:Ѓ1hFot]7-J{:_}~u^[oj%fKiuM!U5|Vz6ӣ‹pG=ӫL|%8'؋_/sdgZ ~}tU׿uo&׺}{u>׺O=u[ So k>fiKN~{ߺ_ߺT?ҿ*??6I̿?㧡?ֈsq "1 7}K^׾/'ߺ]SÏߺ]׿uKsϿuO~wŏ{I Qxud:?On&W>RfhEVׯ|??,"N؋{^'~b{zwm}U6ebqYF_=D9?3F~ݕ_.(ZA92ݟi}t“f_cɭ h׻3`lVSSX,^?GXW 4vWbyPjOO0}mq?z_A^;*wVinZ EH\*X9VG/3E`g>@td,FvnM鵲_oxM3Bm"HU^IycMGV Sމ$tϦ?, X][i>|{jt߻?2,E= T+@ (V$;k"-CV%F}%iQ_ېGw1?ۨnoaM,G(S:;gg7[^c:|&w79\Mf%WyF[CMpMzSm"l]řuG?В p=Q/ Ŋ0#_Q^_Wl\h_]mLz'dJi7~]dUWrX[dBtȈf4E~Sy=GRߋI'uO/_u7߼X~޽Hpα.O#WwEt7,_?oVf:YOƮ~niŋ^ z-r?wm[GxoO|+w%{\:u"ߏ~cazъZeO=l>9w@t_hvl:̶ۻEW},)ϒfGXb )dYˆn.[!UzZٸn?'XMUP@7<^}mkujWZTw{"mASwW6L_YRdD23utVbT; b(Oàߗ7 Ow?EF?}xS“dOCh2k߼X~޽K'u|"~uu^?[߼Hu _?+ _ONp>zvq)'^ \X̳݂}q#xRg\6PGan }OQ9w_eK$~xR l2vܿqm 6> MX) 3Le՘i<:[lub]W(׺dE6,r߿q<:%T(7Tuឩ_e׵zoͽhF_lb{pV`U'͚H%&;c,譤o[L\읣Z'ҏrw[ryJox%~ճ'$0];+hۍ5nۨe7 PPcG=K M,,~4E![*)JĚSǨ# Y?AQі>M}(TE``|}$7#bH`}چx(8>]B|EvZ?ԡ'x ce뽮`ro,kIQ:PRޘĒiޕ ѝ.uIEWuGϘU4|fi 㭧2DJ "!vVA/>5xRpz/¿P _ZM=ďgPϕӹ^ns>5{O=lU~;vEbdWkanVK 3 l>' Wzo<8ɥ5+0z0NWYoWVոvڝTlK*Xqp9_4¤"e-Zci# zoϹ?N4!zׅ' 'Ǿ~蟇?!ߺcuEw6o#qPᣒ *HZeJx;.#Xni קaā@=z5I%szHaqRGF`WEzW+؝Gڻ"\[nb%Lc5&ı1G'}u>gnv7.Iclqp5 s/PA)IbW{)і0+^"{JPS. ,NQ?P?YFN"% 7/ӯwF~Xm~jjN ۈoל}[oyE~+#I_I HJyJ]XʌVJ//2PQ|kwh`?_("M ?-ED k:ݼHǘ g[|0}#w==&ٝsn:j꣡,sW@*a%$04'6*̤*A%H5*O~??}}D'uv~=M7^,i)25TG䝽+a1 Bc"3DTq"iS-EE,{O+/a GSBȷ:1R9f~,~_?C>{߼H~޽'u eUď^!|o?S{ t!oun?_{׼)A|j}߼X~޽'u|*vOƎ$NDCxRyK7 h>1yh構\#O%EE:GG _߼Xu Cg[ҟ:'78vE=>S Z25SNZ7TfhݓZei Dc xt^׽uߺT?*??6G̿?㧡?ֈhߏx,"1\??]s??~yߺ]=u?ߺ]s>ֺ[{['r>Oֺoq}{*x>ֺ4Zxud:?On&W>RfhEVׯ|??,"N؋{^׺uy$[>}ӫԟ?Q]k^8  =}JL on?Z4?7 íyo#o{ٿuj?gc-xө#l/ HyaXTԪ&y3-{~@i7z]q7boD A-ȷߺ:D QaoW*ʗO{^Y/nxSk`Sˤ[ Al??A=ZǧO`spl ,uOtzTuLt BI~:B,cbM:1[?uqO>?]q7ސRM<~b=թÈ?gX˃`ǎ?&<~AyoS_˧ d`Յ!lr/p }W?gUcǯϻxt@5Qo3Hy~4oO4X@Xk#=lPWn?V$xgW^9n-][:\^ǯint5=2梖g I:A 1D[Y+*up7[_Vz̘̖ ܑpܳGW^]HLGZN)?`Wz@ʓGPH  ) G׋TS+ NӤUYLaݚrO{:}z1 slT<PmG=zal\qa7A֪z>nIACǩtA ^z+Ă뵾'~ٮ:rQCF^}*ױ,uIĥ`O&+WϧPt}T-*n?ޭoI,TZn*8%~ S״5dk%V@P%E#vDS9PәMC=uNwNYI6 >}`|JA݈[؋~=Sz#U԰Khk5qSϫP9;.̥)SΒAOMzBՈcQ}89?:3'7uH??>֔W6szŅ_{P+ç:O?C׸uݹ_v:׭[oa=$1pyG?5ۑ`{4gt1b%34#J\ٮHTQ=C 1{]XTtBRm68uo< oO?>_>u?}}ux?:uip]oaZaEگkX ':h׏A"7}X_ގ&ѩ_Y$ o&$tWu$[ǿut1YMżo^NkhDd}-ǽT۵ + *4,A7O_Bצ="WH|brٛ_4&5)щj:4 3H5 fh:mwW[O{qc0_}mSqK=4u] q!w*"&݌2q-琤N~U?ϧ'xv0b:"#jkӽ6XX ,bk_OPlK@G${K:ykҨJ 3Hi}NZ[PX*u^U<h{;Ѹ,NMX%FS*~MQ##ޅy`%NIEe'a#޵ׂ\EAt ~?_ͽ[V:ޑue>,~zO!OBӤ(U%G }ʽ4MN:b:e'U}> {}GH]Vl3yR8_޼ӠR?%{sͿq=8F?+czߟ{Z!m~>xt?ֺs}?{ֺqX?NQ7<zRӡ\ W{]{ߺ^׽uߺUY|Ma;'%OAO'oz~MG-D("WHEf<}oKq] w@IR m]qq0]# *1'׭ ^]ݱv jx=@~/ҳ/0 ?K>>O׏C8{[t~x7 s۷{'?YuΟֿBusۿedo{s?_-Ч]{ùӞC?o~}nPwhSp\֮ܟ;wPG{=nP_whSp\֮O=?PIuΟֿBusۿ({#[=nP_whSp\֮ܟ;{Ed\(Wk 4)8.?W^?=~_3߿?jo{?js)d_q|(/Wo 4)8.?W][:]`T#z>s(,>A<œ7 ?>ؠܻ=r3Ȭb&% ~[+2ک1XRhdI y6 ;k)$pƬʀv{eZ\xUH4 |::uo^~0No,go G:yu }zuӞ:ʌF^I=蚞M3½9V4\#zIN"5'O'zIR[I 齉*CoI8=WUN80(1։u)_~֩IŒMd\Ϳ۟zV\~[\P$}fEp6^ө Р{^R"ơPYE9?Ss) 7!X O^aOo~ϧ^ǯ\SRH %@ މ]oIzP\l?Aߛ u Md؂to=GR'!k\fǿpWlFTMA}%\ <?>N<`,ybs?z~}l?o΢>+ƕ?@nOZ{^sv eP'rOϽ^+Ѓw!#Ɓ?CPӦ"N-Ζv'c•.ֻ.Z߀_=zGX?}6 \h_x7o~uڥD'N}n_G㢈./٬>GI.a#[`~ޛ:?.H?ě}x:z?Xr~u?[X[{ֲ0:!7~[Upy[B~ݍ(T.Tى_tVaܓ?MrO~W?Յ@Ef-\_0z&ܛ{ia6͑+1X(لHUծf!V 1H*p:!EI+3^:o#F{ߌSߝDA,9{(g^ca8/')(7am?z~}“δCSR:BȾ,m'v ف۞=qz=GL]}]7V1cwa3Rj΂wHz˖:MzxfpwYqL62 *\ln>=euS$qfV&f5ޣ?լ?=Ó=Sň~!ŷw6Z=ŵsˁd`rTy\eb+cM]B؂\0 ؋{`t`SQΦ>{ח^M뾳>{UPi(wf`ijh)$̩ronF V;S"}Ui~EI# 9Ean|"W+sT8VlS%ty(jaL|}ߥb1bPJot5@`(;) @o 3)=d(ʘ_Rδe~!GR1/U cqkHʜ?_M/~v, !T af;kքNpW~ܛR=:ybp0?^G\e{nޟ Jr,dqp2Y|L%HAE:` zQ֒W8iyyR8H$ <9(3,>95tΧY X}L$B+Մ4$MoWz*|C~Q|jlǽxr kƋG4?'[o=/h~ޝ0"q0Xg)P^?zmNh飜9 ,ǀ nhg^Mߤu~:U;jKS/dШX*\%/ko,h~δΨO_2, &MHoJzNd+*>aS_K-nڧ:Gf7=hHA^{w&Dk"`mRny =ÓVY"?oHxT|jXR(<[{ί_u=>Ug~uckGxoz׋Ћ{{*{djlK=}.،Uzj ]2 emrozeeV ӡ^!{[=9UsuwGXm1 -Jb5k('W(~Gs:lȀБ^N;`p?߼7=kĎMO^߰T=,udPOL>v:}Ð#?oO/)U_.^FX>gm*9{߆u?Qюj*f1,J+py}.>ָ I$=׾Ρ}~d\ {V똠[GGGӭTW:1 _z^Y" }O{ZĒ_XžqߺXҜ~[}x^XF?^Aï^v8x&s{ƾu}dS4?oּF.a~}a5p`O~ɒYQb<zu45-`AkcUMiJ|8U[?IR }z:U׺u{{^0U,~+o~j4]"ָ'aoEqϦ 5Zkxc~޸4r1Emc9_o^_WX.ucÝE$oZ(ңu&6:$BWϻUc@?G%,ߟUlnG?Sqb-ŸW\SXpBܟ bF r@/ﳯq뒩%Vֹov'u5:|Q,C:R&w#- 0yë.(?NA{ 7?[ׅ<ܓs\}x}ǽtz,q!]!RϿ4=k/cT1_)hҸ|ZIsI[ޏ2s~}$K4y?_W?^~~-]kAsa޸Bml)]{k ?ß<̜?y1zK1rV:_ 4{z K?m,OGx:7/G_S׼}Yz|Δ gb:GK׼+=֛S*7F;+dc^\,_gxtޅtю=/77o[۫r *_LF_1r gJT 1(Lz.ݗۛKIP[fc稏ٶA8-6.Dg@brbvoۖejIX_q"7*}傧?rnd8Jt|hߙyQ1$j؀ʪNX 3EM_Gϭkg՞_hu'PW7EX>t5Vø)ka)vG鬨3GQ93[NJP3XU_3>hlxj6^{6;_]-׵Kxt,i*zAPhg .>g1e:KχG]?:kt j6Vm }WvnjhJ-69j3Ӭ (}:r[fۏr|tuf-2-O[$3#XZI%@'E px_Q*|_c}*CH6P?x: oc ѾK׼: K?z8?~={u\7>Y7}sl&ٸL&ࡒ]ȴuEd/%k]AC#EL*U z?d1_Yez ۿ cgj_a"/.:3ϗo"Ǧ'-= I5GM-hʏzqBnו.<Uf{Sټ3(6ކ3K 4 5CfVzgRx vu?^nn&r}1["nl E6 z!fhh38fҼBWS|M#dNzñ{ǯ7 /6%۹z?\SQjN:MԲDJ85Jxgu oc>3F/Wb oc ѾK׼x|X1ox^"7xcK׼i=c:;w^|N{imx_3wQl\mvŠ 5P\ to?fVf:j*[iݡGUzYI&u$T2/&@? WnܻoSg7=rV%=m}}ueFYewvf? vId@4ƤӡK}Ɨ7xcK׼>,tx7߼ix?)pvRt^oٴ00a3URdhȁԐ@6;淚EL$zP׫;f?۟hA̞+9՝{ޟ'e.8~Xo{p66iR+ D!b1X \g3SNgK}Ɨ׭1]wX1O~ _?:_@o4{z7Ŏ/F/u/n/f7)4{z>>aOXbwM۫(F?.+|%1bcu/)7(\j5ꪊ+=Z!?[~/祤oj|P]C{︺[`N9ݴxeiZhB0Lnğk&wBM0:/4pK SL{gƗק| ,t7߼i}z7Ŏ/F/^".]|f,nS[#:*Z WUG[GSXE9+A{K!p U! y?!#EmDS]GJ%˸UQèf(cH~@{ڷV%:=޽{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u }H{^̈5,>+{ZX?n7z4V- }꣭PDɨ9Ӯ:\ש^r@lMߺ=OXڲ{}@n= U1 G&{z’V cs6݇Z84=tF6Eᆒ u:kNܕ`%~9թ~Y67#}靪r/U>:_Tbؼq5ɈpM|U93BHf_CQ>^$3[=Kk~r\P (WzIӛ]~$CQ{Z a9z`O'xq|3׺M 8{~GбSpA,oA'6rj \T[dk{>lb&?:~b}˫|cm_xxrO~"k{gg?Uq{zls͇,IA䔚 -mǁ(O_NfzM+@Չ",\t ܂Mk߾}{4Y@>jԺD\jRM_@ [=Jk<Е`_}<_?Oߦva#:/?v.ݎ3Y=@l5$-]{^ǍzhrУmO?jV߽OzlV EIPǟkiQ_o#T0m#1ib6.㣧)U4P6*,tBR xr|'tıCa>L|WEn:}O%3Mۣ W7zZ Dn#,ɟt@Qn_n`kod|"]W3VəSh ~ĆO!cY*VGL릪(ljF5Qud_+~N7]Q 6N53[yFc<:o# SʨD+A A8j.oύ[n?(c+Z9MNB,.۱JXR@!`?TVG:5IvS綖vV>#ҵe$Wb#SfRA *\SzU?/x^ꢥOGKܻZHAzhVC4U[9'oi\~ΘH~gT|foWv닿sN[wn^h2>>Ң`".,nGb‡˦'H:V}_oo ue^>;&jJJSG=-T^7Sʺ0 _گӲDO˪Z>>anr˸Yl͝7%,\)RbJ`,=$+)ǪkƬթV?7&qdq?y~v{7tQT $ooefJbXBA, &Bz:hje1<#YX\pENuelI᪤[fTx&G,3Gee] vfdMFU?~ҏc= 쒾5?i}_fgTs?W{aU1} ks2IRJjUF5m5'i+ *[>M |[}_u?={coxkgU'{yv̇Ī~Rh|Tj*R=*@^ԮB<KN9kzCo=ݏqX[֏"Fj9}6Zm^o/'`,̾qOh<HAe&T7P= Q֪'=2v쟗0;Y&{֣hEGfbJ>)U\1W9zpkff]]s9\AV)tճ/,\=oMTT}߿,Tb7&Ƞݟ.7 x}57PK0iЌET[ڹ$JȚݍI.M֟*~ntev>JrlCz`25׆EQ%@#TԐZ1Ԋp2VST*\?Jġz_՘>բS`Z6(>:Q\uZt{SGN3=`5i/h_+k>ׯm6o=LosT "4mT[w!uUQQ aS_Zuu`zp7q?۞kW?^ {oG+gN;{p]2ݿ0nt½^5C&7.F5HᦎDx0lFu)#I[F~2ڻ 6UXtWDrmZu2*%I;zH}rKpzY\O)[_ߺ]^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~qESߺ]M-~u)</OTl\g'~9:oBf`G:lxX~?Gjbvm7מ?>^:$so{A~>:RFkQck׿y(:U[xG ?>^lBJ"ן6zw>8";Z;XA-W>=T2DEX u` _kT?C:uy($_= aN?\ooW{[Wx^A<"jRǁ_wzm?J_{]{ߺ^׽u~a\p@z~}1}Vuo/#iߛs>sZtt@i&~ƒl?\ FlE,5@k|tP ]PA(?ܒ@cӭWAa~S$_oQΠn;,a 5=@h:AE}?>{ح3Տu#$+؋oo{I:\RDRs? EPxp8c`sRѪ/o~}x (@)[?s{{ uS7bHp~} uM醫a(9 G`ӏ[ U8[Siԕiڅ1ԄۓH.ɰіZ, {WNT[fXdIy,8,/ p?ZW ㋑?O^j0 S w[޸u=sz܈k[׵Ԉqu rA<_~(t.{SA 07X"?}֫Q`-kn->=fFϗ\>^os]l2(Q@.~=[kT5O>,RǫZ|IJAOҠ?OL] *F |7az7\~+n)1}bN֥MC?ֿnh~ӱ-cbQgdk ׺#_Ǫ?|/?TޓK?IGҎ|{ؿ^d/iQ׽uD=n˼lU౒b>onXW əh60 &YS*TFQIn+{KҾBGWNv||-C4^8z笻ujؘԣIf KI_//JtڕZZFR6'Jk5Ly-.-}]VL/\j?a-|/ub6N 䪽/WRd~Ί$o_yzozFbg:4o-٭=;k#{qgT{[*T1b#*#H )ApԒV D&$?I>GFN F~яߣiG7сgdY%Kh~ޫkt :3rݽEsm;7lᥫ]t2UNʠf}#zt\1//Ϗ_::^^$?h|RνGCǾkp]37f9(p;Ӻ*c0sհ;hҷ[4鋆Fj:M;i,QM%>}/-}3mcp՞6:?u\lW:sj(F\o_|7@G>\q_tob:κ3??{mgx*i/"y={?xgO_; ~D|Vdt}L=WK>&*饒W(GlB5#Y#H9Жou,KGxrQRU?pGMu?_{k=u߿uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽'oo WӮvqo׫6'p96{[Z?[6_q@>m#N}h?{{ՉzpyuB ZxtH-i[yuSǨԀzOޏ^_z@OӟI^\M)Zu K~@G\D?hk\Yy q޺ϨGU6:@^Mj׋o}t@7(MVl!u\pnlc?-g zt‚H,m?~L׽u~{ߺ^ZL`M} }|AKGS+4SթͿ&CçA:P1Q6AYWY'?=hǏ5\Gmbܛ~ySjZuqhGqpG }W6}#˭jJ{5gSW ]<(e]QmVuq4@%ɷ%NO?폿PyuqA73pIW˩`u0~TG_yEJ~Bx 䩖?\P:9sq_,fֵ\_ꏳ_.^#ģcfo{^".뮁pX^ꮵTfVTu9\Nct*5T+ E%OaDqy_ zh?GrA;+uv>嗭GfqbKvR#%/"#hd{B4)V:`Nᵰ8gXマ='z|EIٛO[$\}{[%dӳ%E4bH#mq(tYIz߇ػsn(èZnUFHǧpcgBT(&F5g^] }{ߺ^Q,ϧ)ʚo/nX6Ŋo߭3s:lt{^׺RBF~Hsq[/s[i?o[W%Y]\P[sn?~}RcHX|C?$:y*_{Fz_ٯ]|\?,Ov.[Wp#G-ۓە{cǢ‘2eZԚAU`nL0GL<.d, /R_koMּ2-"[ivCO>x2 :[ 6+d}V6z39og4T$YM#ݕ^cZyVFi_.I;- TĠX^Z56UӚ$0? 3O?R?sBHgۯ O]lI~=ν7ğoJ柸u3@}d8MBIZǺAⰟނ|_C}qM~޼U]+I N1ڽF[yNI>ܺ.iO=-~o1˒Iۿ.Ic-}JЧ?׾R 5/[UyԿ:rS=XQί_Q?tC?kʎ{Sq/;o=&{_;Gq)onO*U 5xBa 2H#aj eTBQ_1ץ- (z'Clɧ`S_0.!u5͞bu:M?K'dS8V<+H)$a"U]qdSGkzTA%##6ԁtb~Nyuߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׏Z<:NxW@ <?[޺=xXqryA?#ޏ[?:_9nuB{4x>7_?>N׹6<}E[o\8qo6=O D\~G}p}7i_\lc#~E_߱׈~[ޱ^3 A?S}{ "m{GӦc,l O>Xu=F̓PMZ-[Օo`E^kV/?Өز7lI ~&`p8E UNqs~>ួֹŸ>}b:>Qk [Z:|p׽u {Tu#Rl{ʽv-Q~׺ǧ\B7?O>h?+~z|rAo' u[OZϬG5L/䵸`GQ+Np̳FZߛsq5vi)WPKQ7Tu`"J$}Zm?_GZ+CN<H'=lQ6{aqbuŽSqZ_JXj -n C`}?s35' c·!TYO߻|r1738-`m`r?WuI f?n-'~/oqoM qyIfB/`12NRuv¦PVUQT)@$Fᑀ @7Ñ9d`@?GWbXAM?AuƽR`ꏉK fo NYZ Wt%"Q) +bA* VX%L;L||nܵVn.ܛln\M8⦯Ti)T#<f4\a8[^+֌$ɣ4Gsi|z=In<"}^܆hQCBHƑcX hʨªFݒ:WϢ_հl-Uev>[`<7JD&u2GI9j!Qq kJ=:vl.f-ʹ.H1R=ݺ x4 b7KEiyHtX>xmMW av]fum|FK3u~( Į55 , OcjF)=,BpH:xuNgܹW*7|g_s'޵̶vgSWW[<Gk#|A~Lt}Lӣ?cv-|z=5;!U&)U%7)HGQʤ ê,MFSF0zsvoHao=>{e`M9 A$ B|G ֪[}U\^1= z,m??^uǑΫgS#ׂuN'fwOQEI5cbs rYiVAQڛRlT1P1:ikcJZY¼r!h϶$?祑WU"ٷ褈n P coaWYT׺uo3lgzc7 cp φRLjyC22J!"dM:O) "9+s`9>4|7wfޚ#fo:ZL6Ž͖d"Dg=74"CSQt{^׺UY2;*,?=dӯ}9: ۴5_6;CeżjtI!Tmُv"*YPyӢ#ws&]vl<7%FQeMG }t9T:?oC]/_Qi輪z#׿oB}c}D^B'Ϗ?*>1dzO;;Cvo{ifɵ膦 )-A.O"}OCSJ&55\d0 i_eɾZ ;sb|Wf=o]=Q.ީ?RH*%H ڽKȹ5p*>bt̎K6n܏geŎ @mp2̅Vj$6_Q #hы⣪+Lk?"6Tu][QGZʿ2q2FI"Ó툍%U ӤN6oknn.6YsSlT}6HhAFVoƷ >A@'V7.ܨX=Qe)&7eD܊a z̤2t"Qa~Ltÿ S9/qd:{_jz)W7.Zs\u&vZ*%H jc,Qi!]zeE;kiگ=Q|Y޿3}KӝMY}ݷ>ZLugi35Фje,#+ S4\lKRg>%l{;rfOgo:ʨyRk<ؚ *2wH%dJdSJW45=/x?/>y}?Ϩ^O/{aG/oQi輪#s?͟V>> {wAe6M>9*1ȈTr@d@zR䏟F3pa;_zZjZEV-g+^Up)ِӧaP}:< {{oO\u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׏ ~#b!g<C=:zCֹ]{j=::EOuTb8O~^D+u'x/}?}gKKۂHSW @U=uA'?^A{$?R}g׮ -T{7O[e~]po7mro_~xֺ5Tp7`?{'NWS'߆xרkJ.aB-mn$=XuHIYTX_PZWy>];OBnM~ J~}AVKMmDX <_{4lQL -Ɛ%uL>ޑ#{bzNyacmkN1^ߋksn-Fz׮A6roy6o{#}^|U`b\mk??>\@i?Ha?>zҌ &7C:GG{E:r<"[8{ߝz,?S}z˯~8f 87iblO{Tc(3VKs{sϿ/\6?_mNgYag˯Sx}nA}Ͽ[J5*dUI$=ϯp=,vA'OoJm$KXǻ(OTltf&ǓP=UT(YuQN8XX~{{[뿠 O?~ָ?s˭t[[Ƚ ?z$pígCV)m ":KϽtN׺u{{^׺u 8o~"r8OcvV%AO+k5=ӏ[ GAFG#iR>Fq⧏NQmxSB:$߸R4av]Uo?ߨOU?xk crZ xק(hz}zu>1o} xx\Z!}Ϫ ?>׺sqoz=kJ\r>zPB??O~]e{ߟ~}Eu.POuo~]r Ooߺ\#ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u \[[~t뉑^5]:3}?Gp׳u`MՀ P}uzK7 ^_^k]C-]nx}coo~^?}?pyO~^TUkZK\u~{ߺ^׽tFi;\ ۛןzVz@}}{뢬8[H7狎=^FI&ڿ7v^u?/kۛK*R͉4,~K)FM'ႝKK~==\rNןB6*4[\g=xY$Uk?6h gT=4E&Iŏ<{sa5݇s=?o=o}sRX#kMy=qd>24n}9gR/RGeuX꽍zA?owQN=QoJ,a ??_!ZUCH>rIWNzciRlMjxmi!]#+si׋SSca'P?u7߱תGϧZJČ`q#zEA#?ՏݺYuZ׺gA%6>׺kxߺX+J!̢ů㎼Ez,1Hj*4?ߺ1&TWPZ}m׆OHCK/ٜA6+o9 5Io"މo[iN5IjyU@Sp-ҿS{o>qobW`t2D$X\՜G~ӯu{{^׺u{{^넆zO# uX-fPGaGutQǠeA#on8TéXhz߶unN׽^*yk.$MϧUNڰ2'ݸop;z ^ a|T-Z=ڞ5Ņz uRz5Y#>,tdc8?٧Z.#װߺK]8ůֺyi>M~}LJOmߟ{UJJX-ǽt~]yGux=uG? uߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ DHI`~~8S)%?$ߓkzk<dj9cpO ۛG[^8QN:l7IQ8]'r! ~9 WQ\Ϧ`?Q,\XWϺiu@nI!{q^kWGא]?o~?> ˥NWe>Ks{}^)A'׮`<~,mm336'@@xcO׷QΚg_%젖#Q?QïS4_X]k)k(~O?O筎HdU}el҄ۏWy` ~{M50fs8 ~x>} $9*XptO  ߻<:}slH'?֡ZyAz$gy;q50X?Rk֘Ӟ[S67Q^+2z"Ozz 5K\|=箆Ak؋P_u:@Y5ܺ-vX}H_l1=((QO7pl>ݺh箲tZ4hWF5Xut̻aI %!FzA$-5d{vP kf?_QR:>C07; Yq|R*Ŕ֋z.xa&"׫QI aONxkxt$܇b9u$qQ{ѡՅ@XmLXܗ{W=b=I&GѐCoM<8^7ϺE*A:P)_}eV׀H _{=lO3e`5+3ao?f%DS[!VzOIkb_PaoS=oNz&:T^[\ߺ>Yxi?kQy?.}hO*l~SկZOGZk{Zo71LuJGXߏZ& ЩYJ=؁?/N^׺u{{^׺u#7/y8 os\/<5*(zז~mRO'ot1`9Ç]{mXu*>x_]"c8F:`l\Udt!7Eq :l=- qqN~?NMz]xn}o|(~Oȹ߳׏6[~?}|Cj[nן(X{xu~{ߺ^ ?CO=_r9Rċ}nM?޺N׺s;??|}{ʽ5& *Em$8@PCR>_uǭ`}#r-RKqq~Ԁ>⦠tp&^*?qu?u ?Kژ직?~O^?~}sTYFv$굇#?؟l5?_K~)Oa{[&oM +<_WJ>}:qOIX?M"n?FFv, ?K\]$pڅM<\&tEŭSgaO^z_Fр?Lv?R}Nl3D8-: 7xu4>1NV7{<WÝ0Ƞ%<c#jPh?e9?'[~<T \<Ity$`h%p(Po{!_~8SתiN.05XV1k?~AǭTAK͈X7#~>_~կ\HG)PA?O?~ERljS\rW>^Aƪ@!c7cPu]SoU)Ο㭀|w`_饁o{IN̳q~Y' pM[-Orxi׀LGwŧ҆,6~:^6 0cE'-k>H\~:ޑǏXr P#U ?&?5^E2zqr竖 C29b $\_#ߍi| .מK7lIou4f<#pn-ϻPWjϮ%>~}wױuߛ7?_ߨgՌX_u?WЕVQ-pln>6=@U`5%u؟tp'WNۂo4n,1Eb~߾}xp@䘑U >]H#9uaW׺ =}1Mv_h7ԅ} B By>P%$jsd/ -C}7?~{ߺ^׽u~{ߺ^*7+nO}=ϭn$B+IvfbTnl-kNg|.BE@x:߳sԱrR~@G E?תRu ^lE\q?ӟ~"S׵+ǧY#S,,I[ZZӔ{X1ၽ~z|MD,Fz ( 8W]W?!}eʎ:W_{U?.d$C}u4nu21c{TG[oTzHXbQc@HP-~{ߺ^׽u~{ߺ\X׺_ϿuߺYulq2?{Zߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^9E`~ß_~B*t^w>W: *uqbP~?.MGO}xp隰97ǫ(WQN {[uVUְױLƚ@Qe#{ן^Oo{^׺u aa_O zey.<n.?Sֵ׾Rn<[)?>ëW({:@j`N~C^XͿQR[ߺMoY_S G{iNQQ H*ꞽ^jaqa{G` ~=q9g1cZEkl摪 Oֺp{an?:>5" IbGcd@b:xM*ztGN}˭d笱ȴvHǿuIQ VE WR`9 aXI?)=a9j@m'GMUx:U"\WSu}{IY EPAFP8|}QרzpQlI#{r?Otp*-L̤ Zu}8W_Mn Dj64ȵz֜u, @O{zOB <[0ȾE3}Ԧz^b"\H nTO.@rTI FI ,lI<}=t3Iq4${`W(t5\|4q{=TZǧ\/Ƚ9$:}z5(/wPo[׭^z\ױœzlHy *MMƣ).ʪT]xnР [~S߼`_-o6߾εtXn,V78[*?[>Z(zKn&y?79 Obhs?\[Sg.珯?_`>qzg5+&t,H}ϭ*0"޲it+{_G6:~{ߺ^׽u~{ߺ^׽u~{ߺX\_~Dcrq{I_~]v9:{Zߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׈.?ߺ\tJ} [ߺ\Pu~{ߺ\J+^8/a{Zz? {^z*B?u{^뫏=Tuȟî&x[cUt=xO}m?>ֺHPIO{^)>~9_߼1^j -j~~[Utd!!jk/o{T>]MfҬֽ6~=tوĆ1>H׷ӭN5 ͩP۝_~zQeȪ)$B[.=I Ӯ4jdTpH.AaK7?_.P ץ(f4:"Fk {MW&*ST(?FߘYmըŬKt{{sAN=de*-^pJ?Vi^W fzcJ-k]$p|nX۴:$^W?퇻ΫunlG%o{K={2#\c.r=k-c+~J؋0@Eh?StJ9:~?}HՖm+o=|zk ױyӮ?n=պPՐnX^ kshk׏B-֙K-66mdn/sm\ҼcB.GH(?_Ϩ XZyn7>׎YhUark\ߨi=x ӡs)*qoϷ)^u?҆ cuGS^^>~~[4/xu?@^$\﷮ň@xЩ:FaU_)NO) I&$ rWcrx݆MW=ba"r׹<C[=VO6<Z58KAok}G]6љ}+p "y >:z~!QͿGMM5dfK'?ގE: g^] Ւ5C-`D_uק5c%{{I]HE%[M=x8tֱ[b~?֋ųb/r׹KXZ{9UA\i< c{N$uAvk n4[i#_L?iaarN}jfh hH`߫WܘLckHߪ+N623m~ӟǿ}:SZp??_߫c [U.]7kXqn]Ttݚ(RG s˷{*z7PLR9+oȷ?Ozzp(#w6bb5H_e$nooͽ>b{J7>O!',~9 GV;UJwq/XYAǶ=lc&V~}p"xZ8Cڐ]-PlP~/M:Fg?}?}O׽u~{ߺ^׽u~{ߺ^׽uOߺQ6coˬ{ˬֺ_}{U׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׉l?ߺ\u'Rɱߺ\USn[k$~qY՝cH\܃tzYt5[$=zFzb>/{o>A&WcqoZyeyKX?Џ~Rǽ~FbH&ů c>պobA?7^G^~=Hl.I6?#˯Vzw1'_>xuQtė ,sXqoV8.8oPZn?>׍ A{uNbE{Xc$˭qS+,l?]xt,ޛϠؗ#{sH$!~8D֐-fy@ n~*?.ӊ,%/j'_۟zK_2\P?K&ߛ{kZ dUo@_߿טW' J66A^mrM݅@UjӶ\N}t},OUp.L/ Ipf -KmȿsNr~\ _˯RfZxQem@q}zB?A{kVΛ|}$pIk?}#POv3ϯZ 6{[:u[5WͮM=xVXBָ>Bo7$O-66_}ոӧTcL^I㟧˫!"}/>qd%mr>oítFjFcrt$Ssa;b 0_-fR/#v5WqAUz疡1F`.~~_gIv_ƮNt-a>YoVkӊ>'jo?y}eH,?:=ϯkR GO Sm'x#'kAG&UCd1Dvn4>}zEIb>̪8s玼:|HP 1/r}g|0KTJ>X=\8tw81 YMs{ӯx~}D{R' sSi6#?H*P7&Zc:Ax6o_{EKMS#; sq?C9S7䣧 O*J~?f@W4W;-<^r G>)IY/uoC#=z;U8TJG?ހcx+y$ck\I'={.+#oA={&ۦcRrl'U&t㪱C=J3iUmB[ݸ_czJP:? T{oFYx ӆ'P?Kԟә:W^~?GvOV:^) >}fmJS>[4 }OcSNZpVS{N{^׺u{{^׺u{{^~EsſypnߺYֺ{joߺ^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ ѿֿc~w?ԯ=uUxSo}~cuu3޺L5m}k cŋjqz.u@omy]ux/s{˭cď{YKH',n`8<<OuN :TGSӫ wPJOaΨ:/e @װ^I> :p?kB@r?{zhtH,`?s_tzn2F+ )6F Ms4=\gs3Fl8d?.=.r>׬ȮєmgXm7Tj=뭌c_W^"辥1z6A[ޛ^\,DS7!M* {oͬ0 ?Ikּ;}!7&6Mߟ0:)>R 4Go{}jPa57x:H|EUB5  lE(zgXMOA?)LSlEŀ6=uzީc7xE$\_i׋1R]y 7?_ڱTf0m}S=$*ԙ%I$ I< cTV@mHV* Lߩ׵&ۅ8 cc{{{QJQy}cORQTX*c{ߺON`,㟯׺$zHcxz~}@#J?Z{zxuc!pHa}zޓǢe }.K^9Q8`0l?7i>]>W$MƐ., kk[NE:nCJ,:T%6޺N/9gztt#B &u(&短߀AzMVP{-?[G[ Qu_Y\߂nE|+*^u~?m59WV<3бO#BT- Ot(s]XC` #BaëHoTؐ>n}4q7"Ó׳amoߺ\O* <[=^=bàl<UW?8St?a%)EȵfACmtU 6N#Xck?M?ӽ{)Nz]caG[P u-9vY2)P[X?_߸:pC>  {וzoi:YM® *?>WSGNžGz8hր뗽n}R(:\)@zAbH ܍\{}h7`SB ָٚ^:o2x *UQPu^׽u~{ߺ^׽u~{ߺ^~`11?o}uB=uF?ao~r /~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~qe ,co~GHu_ߺRtQIJOzW׿>_~=u7']oS(ϏI }?=uCçY?ͽ~}zL^Xx@Qۑ~mȷߺ>}xāԪPKxs'}hND!ֺd'~V1/,>O^뉵1O޼]av ٜ?OM?՞o\)68`Di_~=߭oz:P9Au7Բ6_[#_}l_z@!lzف7 'l^H#WJꕵ8S Fx=#-C>`*?텽J/] ), }OӮBrNG꽈sPP~]HQ,ll@]LK7ӏ~~Un*)cu`WK9v n#H~UJکç&崁Xr?|:5J?05UË{ZmnIQ\~ɯ Z@uxci ON׏\N7Uި[QzPo'vU7G^믺Av%`Wi>cl"ZA` *G>^&F9p$~/oͿ?}ꢴTnejX:bnsp=ҽ84.~_{Z-S ͦŘpEϽW$u t7:+Z~<}*no:tǤ&IOU[\x}،Յze#/i4ױEӱOQkpy?6-\.UZ r6R8:xƣZ5_Ő;i*/_鎨ǯH-{ĩ/߀Քc2b>=sՍzM ޯuuʨlWǯڔ{ETeE_>\W5)NJS–tp8Qϻg2zwDR/f+p\?tj.Qt*X{ul-{|^?_}=׼Uu|mz.O##۞GC Ap.XE/6bzh MEbJ7 u`N: =λ u3׺^4M31_ ~? ^cáf@D1b6?K[̀{c5#Ԫ$6ak{5bO PhR17c{ @>ONrkM߸g-G!'jjEz^X+>[ =lzuSU= #5/kOױӖ7ZjbJX'}-~_=U:𐲴D-Ӥ{s5Sk{^׺u{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׍x׍"׺Cds{=,Xɽ׺{fy$`JqomDחN8oը}>?ߺz?Q~ZR)MO`Q?ӟ{OS]Ht_Azz2pu(Sn~{)׫f` cMhi dpy"?ASׯԉcmPW2-U~߆~[z 'TU=xP '{XNMz4qa}B.F Z+~ k=u i`u잼(^ϯ^H?AOb}iRF. G؟~dr}v*H"uU m~ួ菏>?<~:qQ{~z rSB,WMN{NL8EH meG:q2J@]UOГǯb8uxnm.ҧk&e4_P9_ovuGu^mL +*rC?Ns^&tTGaoz#*8H?v=o: /6Q`,t8ҳjDDR@7qoǻ/k nm~9??{=PRR4$=8+A f#.~+@:\ ?9{Zֱ7_ߺj:y.7~pi%E#*.mjǽƝ{MUIbO څ5ΩS1S8VzX͍6yuׯ{k-{׬rG 6?^@<[ޫA^k!PyR7<[[kZ'qjxa<&}TP䚲,ɿ 6^µ]8:><~Oy6&~{RQ#>JKʞ/{{^t!"RyBα<=:7 %ϣ\Ͻҵك#Uo}=:CvQͅ<[ɧV뗎O%#p>ZBc$Zy, ?PyÏQ5dI<Qsízw$'(mB&X'ߨj}zOn)1?Q{ =J%pKĐǪ K("ۏ{Pc9]&J$\` .-_ޏb`Ȅ'ޡ)K^NPu:G*]Tc`鞽/4mqn\?^Mց:ӠY ! M;(8uFI浇7@G?>*z?,Р]S{Сz~"D%UKqp}uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u }Ht=jub_" ߺ]*G#ߺ\\'ߺQe8n?X?NEWak^:\>?ߛ{[Ya42{~l-WE:<ē{}uP p~~t߿ꧭhը<%A~ձéU =7xֹ>LSӗu{{^׺u$ICz"53_IfcO&8'OϺOOqWˮU tȼs_???qzvfC Z$ᄒ+ï8V:YM_Ƿ)uzCGnl.u-p?N=SW^X6U$6n aϿq ?>Н>$?N~o߫֫_EK@#!<^7_u>}ZWKU<'mǞoI ?Aո-K&  }O'|ˎ??KPV_g]6[A}:tMu<HP qq{[?BlZb[|:kϠ!e-~n_luƽt.Sp#yǽuaulӥB]2#UoNG+J^Q@n'WRp/?=Ӹ8A\ہo{z(kׯIo=byK ן^yĩ M6=6=*Rer/~Hv괯@z[lokmקi,+nd<>x`u:u$M?O~ntMS0#bou5>}i:e*8>6kR| 4Uxn rM܏߅ztg~,-^!H _}ң#hϬRKq~^N9St$mHtiY6?z:.y t,݇$-O?{Ϫt hfXW[AՑSXכzsNx{:XOt 1#{4l:6a:DqHI:֡ǩ+$COdSS˯jJ&ޒ S!rxc =z^;a$uQ!Sޡ4u'P={X_CյT}_\c0pBŤyވӎzץ-q7ȹ*?OOvǪu)pThN]u&+^NTIݾ,- H< Q(66#O߳^Lyt0j>z=Dp.ĝ*Yt='N>m f[?+{[|D'_C'o<:ޓ^%#I'6>qM$xʼnH[#߾έ鮧|)1,$]GXa4˼s{LG$M$8kQllTL( ` l8uCQеgjpX 1R (:w{{^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ] {x[{~׺OIv?y N]lp}}}z$o}{ShA2^$G Ӕ# =tOm~ING'qըkw ߛ:p6>{^2~Bj:h5`%a76 GOW\zc{_u_sn?~ySO='O׷zz.E#A7aYkCO2X$Asa@XGAMuN'RܟqW|pO.-ƣ^.`x&.y<o^?C=տjFUY &jRGߎ}pQb2[C>n{5ƽ\a.m]!}=zk׼h\$~}p>*[i>ߺB>OH,US!poTc2v HF+[#AaW4P\.;QK+Dz5ο ƾ˭dL5c}@S/X>֍:ΘyJS SapS3*_5[{Z'H)U@1vY$PH[[?>S]Gpvkqz#_V[Lfߍ|֯^ǶiP? ߏ ?x^&g]EnN^hX9NT\\b?߼i6x MN_pn/{Hz뒜j.>ŏAZzVb_O }UO[*=D|5Rd}:8|dDT5bA''OTqjhzuXc j Mu YS46E.tرAwQR@<+3M axJO T{[ߙRUK7_=WoH 3'O{iJuFmřx#Cu+,v^9Fr,M p:)շD4f$qΥi}g=6zpN/̺M_{#ՔztV$c6@Ͻ:=cP?[+{B_${Ν{hO $z8oSQ:X]"yB>զe6ŏ"]iNnt1R)i:Eŭӑax龤{^׺u{{^׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ\WaB~r}~Q#r׏{>GXԯZS}4XG~?׍|T@f)>ׇLO6/k}>^8ӯs>?}8xO>F:ss~ I^1_x8-_m{Gs][?R=׺.?_{^ϠX49&T֏^u~{ߺ^׽tə5b. ?p/?Յ|I (:׿7ӏuNd\VA&OM8uӮx$ p{:zG:Խ]&ˀOTj*k=4l9GG}= H+ltH:naL4&AnuCS5I-f7P"׵B  SJ1„%8O׽ V:(iǦAT ͇ď߻Ob?}M~zGtωY~F< #U)@z0RQpm#݉gj#[ʣT#qZS_݊a V+נ__ڵX}8vP6eQeT\_ȧZIX71y걿zԵۨR-"ڮ-KkQU:̛vZD4c6A׵zv"}1k7yꞴMztQ]$܁k~=T=g4J {^:ӭ$Ecqx}뵢Ff zO͔{zY -1O1?k[dƢʈ{Z럿uK(o6${FFڗX6 ~z'Vz.6H[QkZ3JG>>RB{ހzFމsPā.uVJkM,50uO:K|%tN򁭐$7:W=,RȀ\XGq=k҇A0 (ǧ@X eTKI=k+^!%y #H~=}p1v'oo=ooеkB܎cpcrH&OnJuF5^=)vhY 6XOz5:E\m\OPp{߁#u:A'9ӯtGHXs}}<׺pU>Dr@kYzdzBSKY?A{٧^_6s_Ɠ޸z{׽^{Wun=n0ǒ~o7NRqЫi .,mnGb#}R65= i_n/~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ]ϿutXf?Ë][Ǯ_ׅ߱z/{Zֱ=oB?Y~:=f5$nTo~<:)7?^Eë![-u=xG$s~~~ƾ]9CrA{֘d'qߏ^i_oo=yA'-s ~^]\2n\ߺN8qY* F{xk{^׺u{][W(8b[cqz\KEɿWƉ԰Ӥkʍ'ONON?ZܕL/^[ [t`5­ok$Os=:Q_uoH#ߊSJ" J<M^$kAe7}k;v \j[?^U$3O_{i׉BR?%Ou f@& SqYzr֋ǫ ZeDKң}ӦP86pHV^Vzb| t2ǿu ;J$?$~ [{ȯ^*èͺ@G^><[ OOUYWH"?DSֺu{{^׺u{{^׺u{{^*a(W}lNڿ5gmNg/ޝ{ڔ_{^:J]TF*XiSb9:}FD c7}ozSVMםLK&߸=m@P.?7a'/~ϯu>>ӎm3ּjRԎ=$ryu %uTW{vM}dV'7^~ވ' *Qkߟ~^'Quu,u ͷsc{fփm%fԣoﯿdKTj%\_MӤM3 $M8>iP:qzP\Nojj:s mZӛ߰8mG^C{}tؐ?ڕ%?ez]R aoӭ3ǂQNIaM5= ]^׺u{{^׺u{{^׺?W>׺~z׺u׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~p(#r>Ou*Qa{O߿uCE N_ߺ:`[ߺs^(Op}Wđ(}kL.?qp?OubI5q}W߿uꞤGĺPXMͭ~}uEr _ے>=u_~^z[r߈ "[د?w{>SíVR?=]^׺u{{^-M\to6ު:4fiWM'P G^ OXr@$p?Ͽj}oIˮn_6G߁x)>]I%K ʼԐHϽ׭P.qexKZqy AsϽk`t,ױRm?Oj=kB}E5:>Ѓs?=cz^YȺO{SoZOv$b}"Y<zUjJ3*8(A 9*u׏V?kH)m r9$(z1VfFH~}M8cl;Ӿ]cq>={~/7{?>znoS׏YhPJ$ɸO?.r:pˢ'%U?Ac!$2%}67,߁ תC$ӧ,y߼cEUC( !WZ[ߏϤX\bV5?KnG{=^sspX\ߟ׵(:` ؐy8oZ:qeշ2FO@Ju%vdrK Xp}x|u$*FÓ Koz8uSRRUmX~xICk/u̠qU>uX~~bDBޫ~b5?&_Ͽu@m~:=zJev~~/VשTY)/~LFԷ{Z゙׺o|oߺ:V3[zd{^:ʲ1|=p*G>ֺ{Jy/{׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ]Ѓux)?,>Qȧ\[!oӟQ#yV\kP=nyKCq~JuL #$_quuT[zG=ԓՅ+IZ?>:Ln 8GWPzW*GgBY}9?[{ l߁ޏ^]7< DnG￧u|p'$pMɷ}SuӞ>}﷧f Ik&ioy:N^ֺu{{^׺No QN3<\Woq9ӹ_Ϯi#7`-`?7:NҬ=W:E*-kH(pVB)r-Q[\ NIqɵ{p"f_/kQ'@Ҫ@$kG4 ~]x4W:Xy&BX-q{p?}O[7:Kf~='žV.n]l}Χϫ#Q%7Sx@X#ON~ WI =瞵\E/ƃ={Wz^m[xkr.H-?=|Վ>rpGȺr8q=TN'~Iy^:] ]{9x?{^]Acom|ߝzV0AJpO:lk{^׺u{{^׺u{{^׺uM43p7Ӥ\~Ֆi'9珡>}9u?}\o6{q׺'Mooz:Ӗ\I€X-ԝ6Un-ޟ.bM0nbj+CGAYAv4'-U:qNHL@/Osx[kRO Ksx` :=f\ܯ@ֵ(Y$J.Xz7#zO[:sjx/0{XgcI%}_uǯRb@up%8:n >xzçVGzɦWS6'}V=KNy&?$/jL-;I*)H{(߀ +֋ct]6yrnMt--b\?P "ĴB9\Si}b;{S?_bNZ?VQߨxySRy2<[]]O^/7[{Z)S bn/@[H_V:5-"TM t OS8LiCK@_ؒV}9Mp +eWA~9j&?l*ON@>TJZvf{PíS_'Ir ?A Ʈ?=瞫Չ:sk)>ոt!$_@6ou=Q?:EԞ/pO^ߍiO^N8=&6}=ڣcR >}o\'"[$[އZ&X/bgx6$g~{ߺ^׽u~{ߺ^׽u~{ߺ^׽q7-x/`T`|Cm$dsj_{W2ma)7XK=Ϸ/oo$}׃-6rj-sp {u~]8SmtjUSu aqzWWJ)|oA}kdHdU7%"AϿ 48j$&đmv?\{}OWmF"u L{Q: +nM^G?FOB}tdƃw0lLd:MD &H?k{O3ׄW-}ϭ\$]/Q#~P^>]`mh?ɿVQHUVKsvZߏ˪E:@/_A`zhI9OMFQ{`9(z@Tڸp9M?)ǧMony,E@KA[uN-_O}NN?ǟ{]Om{[}OuЗDQVM>޺<?#={ˮnH~utF-s{{5x֛Esӭ Oq8~oc\ ߺˮ"ȷ=<.-ÇTqԿ~{ߺ^׽u~%s'֋/sSL6 k[6NSƿWG?l_?>=֝lZ~]u.<yu_/U >SsԌ"b#_xW(&x&[%$j$n9V=zw~_]b [ y T^~>Zgd#H?m {[@?[ Q_l(edڂT$ {~8㞨xT6aC{=#&zI^t  O6{Gժ(:W<v֫/EH }:E=?R=4dXh _puXןT4뼭jH CrO?u@=0&$)p@I؟}Ov$p9M/<ގ~]l!c"}dv~5Z:֍>op}W >L]*i7{U?u{{^׺~U][uSb_ߟ~{O=NI_3)ʥ `֡’+N$<@G/?Tvb+`@CQ£wx]z?Ar=Hxu(Ņz~NN 9E` ]dq&>5teBI?yt^w ?B{NM^?l׭~׺u^'~>ޯOQ$Oڅo7i`G׿ݒ=Q>)z}\yԧM+t:㷡g7b k":ӇAkktè*?8MB޼NSo.x.?bۋ_~ǩk_/+{ЙF}NsuurH}9&ՇQ׼׼~^]HCԄ?}yuߺ^MGNxO~tߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ =h5uW]׿{:]&]{Jx8Z.ZOYu~{ߺ^׽tW2"6'H<y~`ӨˇQkf7Tu/x>[`|w<\Zbb[dTDR?}^zOHZ:B~O־ιA|X\:x7[~z|1ay{ܐ߰s/?fAޱתFz䔔i{i6fhubCp+c o1uZǃo~}By(XyTUmUpZ678>xS@9<#kaֻr&m^ߧJ:KZ_u:U彅"I{`amp4Л߫"T8?Tu$tI5%7]VQG+fHՈ[nf-͇?Dӫ3F\TuqmՐ&RA=˭_1kI1[#{~??X=g"2MzЌD$:oi8nT$[ztUɫTnMLæ'd /fe7~FAZABX ņ8޽IP}s暦0ebyycP^lF>>æW56_u㎮? ߁~}^fǛ_m f{#EbtjX4 N G&Ců#u^1DX ?_t4=UHW~(R7< X7'ov꣎zs[7,[ڹOtn4^X(6@^?߫Пǯ_={},~[q`XIi  { ~=dV4dSH}TX57kX@f}U7_ϧH޿=o꘯^SuW9?.jUnV㋏??~x.m x<(kޅxtS_>2Ĭ32QoOoϿyPj:.JB_BF:{яB_()׺~'@{xu<G{T}m|xuK`,6x 6= )Z8>U<0zovJK"7>p*zqxO Ou G _jORSӯS5Z=օsԨmO>^ҏ ʐG{%ы?[- }T":`G Zo::Y? >ֺZ׼s}u%a*}uFGu!{׺ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u刺u@")<~"c<:1(,I ~t<:O~mUz˩J#MT~` ?Wӯ_m 8{z:Q?PE]LA" "}{>}B!.U@ʻ\'g^ Zto>F:`[ߨz#:H qc<>AoGA<{?'ߺM,ʗ܀8p7=YT|m9[Oe _ރWVъq7X/TA[=W^Huw|eMF.6#ߵqgiɧConcgα`Gw:EJ>˯ԛ ' [$ W$z=x|jꍁR L TAA7z:q I]AXr@??[zÎ~1ӎ%Ec+M$ >qZ_K*{1Ò8gY$Z[R6X{Ӟ`n,s5V$]di?Mz<<nlǕ<1~ܡ}kt{XmqGzWËS8qp5o|L6ɿBO>ެ^]#E[y=z{֫׬/<~cԑj >}G7?Omxt//4^/Ϗ?AN}Мtq`?p,?垹sŏsu4_o\Y$Zߎ{:OSizH orx젞:˜q\ O?Ov鑟[u}DVx6 ?CN<)m~<(~{˭צ9e?'>7&׹<~_>ָu7 kpG^z fqbHGnI{}2"{ kr,l-n4zA-o:]?g߉Ӄk_~:=qsn >cȿQ^{g1 ~?cǡ2Q!a`"&S/U',KV%xtQQ﷮ߛ{o"}I|GZNy^z[` f??,G#eOg^ s4A"sӪS@q%tl >.z:%}u{{^8߸\ZuܟUo1Kq_3ǯ H-o/$wOɪ_Oz[ {;J1KTI#;%i>?c`u#=Hfqqp7n8׍SKSp.<{}zAțY~<Uukآ`AzHZ$Rzp) PIi] \xǻNZz_I eAl6ZoSEܐ~?5:pTtҖe#Iʸ^.:npxknl?ީTaÞ?cU.xtDu/jSڏNǨկ~@o~ZԵFGa(~޳׫ȱ0GE&X_zsH1d'~]vHAߺQp^<ߏ~G_ߺ랴 {^UoZ"z>׺>׺X׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^)N p@{{qS?VͿ8ZZ"Y㤿}g8AE+c#"S:wbЩ%[.Qk 7!n y:=MQ??}xכֿoog)0FO7ߗU=5bx??}ϫu\q}}{]&c{?_@^:}bQ_6U1$0#{$^Z լtM}L t$EckiMے86<>>.E%lnlN[Qվ_W)5 bE ō7 sϽuREs^F[6g`oo{OK`rE~/:cU"}E@*4^M{8tSo@^-T?QqB[*^[&Z~M:z-/v.?uj֝oXÛ҃k^??C6zI)5.b?Oz>}ZW^F؃b<}>ow(:^?xq?/{]{׽ \éذ!o{-~I8{h էB 4QƒW#jQi[y@Ϧ?A6^zC{<~A$c$r,9/}6*?>5Nʤof׏Ǻ7%iB Va~}{S##>JЏO[ߩ֪\;% {[~[=o˥ڤHhgA _(4W. 6$=BIm`/kIauE"kӚ|ǎ},a}]M]P\qss ({:zpl{\HSKԟ$sry?OzVWާPlv_'MAQ_êCP8nIR/GI~7F&7Է#7[޵^0LLu^MȽ$ި:Ԉ(8qbG{Sjj ڸ?^ ka=M@F_X<.SQ[Y?ێ?{3^SeP iHҷ?8ֺ<0߆^7u@oo{Hߺ\u~{ߺ^׽u~{ߺ^&O~6ULT3 I٫&/k'}?Hi޾޷w6ƽu% ZYߺY^:k{^%~}ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ In~?8~mckr-nmutz~~so{z1~"Ͻ|ϧ@ osݺjkJ^;XM޼r8,>o~˯ Z&āƯTu쎻~Zy֫P~?ǽjo/7} اQ텀~molG q_>uׅnCq_Kz;kc TˤO[Wzx^9}G[cM*nKr?Ә|V$o+kX\_Nyi֩^i}5zM?@-}o^M=,qb׵}K{tTh8yQ`IA~??_W} p׿sSQZg_qW>۟ۏ{^4^pX7#Z_|@_zqq2G?ǟ߻Gtȵ[%<o߻4 QV?Pʷ㋋XgztR_b#n,Hӟ{8װ+Q~Γ/v?A#ku8"$zKAoc[)F@XĂ(& C8uBs;PW*.-bV&qǐ6yPA߃u?Qpy<W[#ϮzޅI)uoΛ@EjڍtW==`g$DCn~`)Gӡ&-*Hg-pOAKSUPGyTןҾ\)y֟^=]xބX /]7^|2d*21n"=\5ӄ$RutSNzzvz;\)MuoR҉[ܩSM^ߏ~:IkCt) 1߫LStGij/rI`>Boߺ9ҢgMAPnOL5BbYVtA6g>}rL=n?db~%`}MkNV5lP͔k{ WT(WITX}a'?>xZgԢ6u2qks{\>׺9G$/Sߺ\ookP/o{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u???{ZW9<z}&I)o{é?uGԤԄ^:Χ~ìֺuzիߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ }OccxtnA7'Oo?u:a6 Sǿu}y[pMo]o7nq}OPI".?_޳Շ]9Cq[]MZ}Rz~~-o~:EEqv?p,/oҿ^]oo*M q{tQ\j66˯pKQ.݇T<:Ld54V >nG{ښ:^&7ĄeP/r,'{xJuEPFC^'~?}l\ŘBpI'>ZϿ^x_,hϡG9)N}zLIqeЂAGuY '8OS˫(SC I'e_>}z֠r1}Enyz_ߴ׵z^5J@6M3B(.V+sua^=YOQA]+k) O6qL[,|1lmh\[X\QL1EӀ9q`W:yPKO"P(6<}\>Xp^nqcqZt8Gc-? Z-~ʽ_P:@ӤU^}Mn}OZz6#?P,~_ߩ_rX/i. Ao~?Uz8JH < H,M=3U`TH" <ߏg[8uv-Z _[{|תY@[M_~@MIK[TꞹŃ)\jO{$秖Et(G,?QЖfp  ?_OyMz9 f^ߐ?7 -6]hE6~x/>?ksud%pcU.yf_zz)m6W}{fJK^ )A}{<33UP?Bmo>׾ޠK~`,X1bl[Xt?Q?PUĆ>yʟ;A,aXu?}{HJ5rßUE{U5'2;Ny~o_z=[M3_eQS b-s~=th?S\sǿc Acs?{=:RUB(U?޺X9. žߺ-$ Ar."ocg?efk/NSׅ o?~^PX7b&[ۑMnuE?Q}kB-O$Ƨϯ}/<ߏ?_~0 }u@I&5$?{:O^4Z.>{^ms_>ֺӀFߺz-akZ[k{^;Lŵ,er=zp녩TF?~]{ws_?<z^^-NO[=g "?MSUy>׀ì -&F珧{5OX1H[KyǽΟPQ\E^}mr/P ^ LJzK_Q:-`l?<?~9:q-MJӋK7$ɷ׀M3On?kN=oI"g@*/k4S)x" o+Pu7$ȱϩ>>k{z:P9)R $saϪg#67<\sz4"5oӁdmS ֕4]XEȷYB~֊q>^R$$_M^]տַVl U4TlUX _Q,nH,? Nר5`T*GE [z^?I?$oGzר8}gI8.}?{^if=eH7ovc=-r =<4Ļ(.ǖn[l.:>g]JE$r {8߉=hqjCI#Mo{o4OuַLu{qkNC׿<￯uXW/bl>S{[t0mu¿E x׷?駭^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺXg~?_"(">Jua$M~}8H{Z?{Ըu%A?ì}fPGԏ~sus^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^eE,>ߺQlA#cct끯lM~Zz9([N#Pol}OUm!5/{X o~=G4z[XMlH^:'H?A}~ZNu"PGMUL:k~X螶4'&b4T'%GPK=݀N-cdžGV&Dzɿ~'ޞ?yQqyׇMK 7?{C@[}K}8nl/Ӄ=d~oqkO߿u=)C?<֩/~OͿ?{cM# 9$}WoT:<~] APO?tЭMz<No#-@V?-o7uo珯?{z5ÏGZ\4oҥZ77kAL:߯C.X $]JuF nPH [֔t G`oZrooOuſqXNy}^ßߏ^u1zŔqoߺhH4 D[mIonbxt׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^)P}^||T-{},xzQIJ?ߏ~UZcȿ^ ;dQL}uB~dx״/^}uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^zy,.l.?ǽ={O s<zaN>]xr/oo?{gS~^>}(#-oBO$oUzk?E=lu `/ȷu굯so z^kׅ$o8 ?y8(c #}%kؖaԯݽ^Cˬ&>~.n` p97ߺޡkcn98|={}?s^GϤp(sϥYI%O\=z}xZ,M7I?gZ_.Th H>$ _qתx%'eU#o=}:S6h$lem¾ihI9flֹ$_hxzVS r/Cs}y?O~j0~Ga CP1Ӯ"pc?CV{GZX}A&|]ߏ ɽ{La ŬEߺ˥Fض6 u~Qכ_˥NEÎq{ݺA\OCiLrm{y}{׾o~:Ksָ WHf'=dߟׅ?Z_ҏ5/\RHݸg^}\/)<>_Lt-~ۂhqoQc~tJs3n?qmW_=NFFX\hR)NJנc2yVoqVE|k\~&zzíuİ_Gq{_ު*A=wK1FP@rx^8C>OW߆zlM۴#I$\jcGA2#>6fؑ}Fܯmֽ9p7hͿ{950y=oÞ|`_>BzzF5 >-:(<>:i׽u~{ߺ^׽u~{ߺ^׽u6?Ͽu?ܿGudn4i^_z^뒗' -o~s׽u~{ߺ^׽u~{ߺ^׽u W#ߺMR#v8o1?ש1߳שQ6c>׾]sZ_?Ou-Q?N>sz}dYϿu?'~e A{^/|}uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^zjy8 =q2-nx{[ۅzz{_@'crM&Ǟ x>҆?пQ}Pt[|=#q>~U:s"ůI/q׎OӂxﳭgϮK^/byxp?i~LXn4}~ [ޱë &Sԋǽ+׮6,/kGO{ꧤ`V+m'̢ާX?m\I$n#/ccr}ڧU nXO$ߖziZ5Q+كG½l^?ЕK^}ӯy^J^"}GO?uoCtOP=Bډ *A#u\3OA9'?߱uつCk7Ž_~ν?WuD-N}}#\S#H@'^ӉV:i6a{ <}~~WW5 I,rk*Ņޝii_]$!_R !7 8z9W\:4c A,Sxֻ?*tH[^8}޾u$뇿VFoOcn?x\s|}p2,ֳܒTXcc?O~$~}\`}@y'%ŭ‹Ͻ<:AGTT01  \c=%z?\ Xv|{PK]G5;Iaq[AX|xQ*X[gs?>_#A=ʆg$$1 >?=և5&wnEݿM<1>pAm7λ."@?B҃{]q׆қ:,ϽRoUz~`&,dX^>U-\cX(C$v=TdS1_Ƞ {֟^q6UQx/b5jmƾ]oPRI4"@>W/i[ߍCU\[V;h׵ܱ&?-[}/Ƚ/{Z96C`>B/v~;v4J pEM:?z621U#p6}Jy$YWR ) ,y}o{U2bh#BЯQvP,|\pߏ?7jJ n:7&su3Ղ8#me(gSSi3tV{0[^)ӴK~>H]%;O-B^:S; 8 _?=eXNOG<{F:wd )%?߫NX|@byuE ?_~"={N?n1Y6b8qoo߽~]ht ;I Q>1\u'ߺ^׽u~{ߺ^׽u~{ߺP*_\ao} 2%a#Հ@Q>^zZ׾]K@.=%-~]HOϿu.ZIS?}u}uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^9nk-9GMK9bA<7xj~N5/?BNC^qv[@ZNttBY[ /nMU&dZ51^׹?Oè_<{:EbZ߀Oߨ:֦'_IRGꞻ`AR^^hP-  ^Җ,KܒM@7@>׵zq`S_~] 1rXkN?O~Z:(耱T?Ԗo鷿c ׏Y3xu#qrm{Ψ8ߺϬ6ϭ+OA >-o~[>?ł zu)iW ۑ"߾}h*SMFGZ=R빦!ߺ+{fqJ ?Hy}꣯io.vp%R^E}zw53#91ccn O4nS7>#ִtYkH :sK9ԠG}W{^׺u{{^׺C,N[_)^QǤ7*~I__?==ZݙR-~9r=QW@z&ٔ p.?}SiGlLH\zY2uR(IY5ǏAkCH-6?gM$}$w}TQȡYx*@#ɸytF?&pIȢߨON*>y?Ͽ}ZzL06ى?MpIS#ߨxu)rE݉'O߸y [b6Xc9n QkbP^:*iߟMߝ_?}OQA;@JT>AJB_${?]_*?~Ӟ0??{:>p(}.?^j+HXp\-k,?۟u=UBʃoŭ}7ש>׺u{{^׺u{{^׺mFZ@>kWw{}N?~ֺߺԥ6 Zu%Muo~]HAxԏ~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u }H=u/mk ^E}꣭6YѨo{^;T¤#k=P'~ޫ_{뽯}z]d,@]dG\}S$.~_ߺq^2n?=A9_:rPG_W߳4A!e[s`gS T/AMbcoy{|:vp-6}lzz#܏j^=ysJ Yۆ*{M=eiR#2[.æzƪMHycޫN.G? &_jO^N=`̈́_᭕I@lqڏ[W-&R$]nm \ZOϭi/TXAp ?U>^BWu+kA:IZos}uecJ5&=ר: @6Db[8R?o>]l3`#"SٺL/cu Dܛ۟Ͽ W8ɦ5_}}z*URȲzbr8%oa>>}G3]kzA{>޵L׮/\Poo׷+שN=b9:T zqIlJ,|ju0[oBNٹ)ܫ {}Hր.y&fL%&OkGW\<%}Öc^}x u罍C}~[O^*:2^f`YG^Iח[B>C-N/_>cgem,W{#qpJ3dK;l>O&=ӹ떖?zxeKu-Σs8WӭRpn@ {{]I-mGПu_>?t,{M{ߺ^׽u~{ߺ^YxnH[x }PR$[޿.8̋pAoz^ׅ?~=xˮM{:RBB6p!^k߈M4L>6L^{ɦRT#Eb mq4=Xb.ok{#8zu'uzrQӯgПKC{Z u/^"#?[= Va`=EGQO_{=TqT ߟz9^=4G`c~?oϻuy׺EҦhuߺQ御*?߲:Ѓ$M$}תWV1?~87u{{^׺u{{^׺uZHcqo z~]mb?KoWf|=u>3ߺSl->֏){ZOV(}Pp4V"P6OzBH?]Xz__{Zu#T?N:~1S?^玫Ǧ8w8qzOq뇨n8"O~z?>Ӓ9@7֩~ ?<ׇOr \[P{JP )PAc{{iC^Lrz-*OͿ~^S_N(T/mqڏRqDZ!Gpzϥ[:moou<:ןHjyyK܁<q㞜 Ÿ/Q*Mܒ/{z7߫Qc+ō<?|^=fo2zJuU_:=WޟO6>.m$|Je}UFyR~SǺtbW-.u@8zߕzx~>A=ǯ>,40[t]UtZɤcsׯ/oO{O?BO~{=:G^6?~7:) G96[3^B^ܹ @HX_8^QX8?j_?M=yi^skro{~?{Nrߋszc>{^㞱Xp?}?zKݤ/ ؿ/o>qB׽~{ߺ^׽u~ЍHpM[:qiǤ9ÁߺWo޽z]狏^ֱ:G'G}m>>>kNy/យo( *5¬-k#<1AUY{^~s_{MԈ;O}}^<:p2JH?C߾CPSFF$Rn)y>Lc2 P2[}޽ m8S9:\qEzuRk׋6&gHg m~x<Ou'˫t$jEhA 8~n}MOФ?}S^׺u{{^׺u{{^׺b_T{[HYgyu ժ)Ԉ'H[ߨzGN1K?Tu6:7O~ZR{׾}JZb?=u!!?u=fX{ ak׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ ?C[C~B}A^ {֏p~?Ib*:%Fx:P8t[yA"Noǫ/oQ6?$I}.^Q ǿyu4Zp9 ?W}{>}?7O{ߟT^5/_u\H8AmcV&y%'H6m?ﳯcϬA~Fi|?6QkkuԼmYI'k~99:T9{iGtCPo$r9{u<}~Q|#q6u6$~I_u/c2ܛu7?_y_Z$ >s(M}?6{ƚM8F?ּ~?zhɏ.mav%I~?؏u48+}.r=v[mqOǺPST׹{H9oN}| S^_%ACaaϻuQO[.A<7?_vSǦZllf6 >}mxt^nr^׾E~~U8skǯWyEk\B x@0U&jm?ٿ~xut,Zuʀ)ot(>6pqc-SIKOz4?<}6bߋ=ҙsf6-~N{XZo~y87'o>^=qǽSKqt=zcU"8:$X׫Ӷx@SR{8׭`3jZX8<}s=۲z5[Z{ 1=X|#^4[@5z@0W{F?3azud{"ltcoh>kP:܀dG>ϭTC+*,l<8ӟj zYT(poo G˧@$y{xV:2k0q&ߋ{O[:6dkؓo/ǥ3 ,%o{sŏ]hҼ ?u^׽u~tH?6׺Z_({^K׺{^\0?{^׺,P>K?[P}uRXǟ~yVqIjk_Ak$߱֨zU8"^x?޽ӮbHmׄ>׺%S@?{Z~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^9CupI fZciߺP%H,mkpocN??_n^8+e/kSr??>W1҄}uAæZj]hOz'^y߸unca]{Kbu"Om?T\ _[l_ou.<ߎ:7>{^=dǒÂ>M8xSIu7b/{쑎OX I}}OU.I*O_$ A''=8cO^B ϺV)ҚNi?TKSz\t/BH!T#saW^?oICbxʼnԑ{Fku ؓrJw/kASցϬdV7U[=q^o_Vz[E^]e]Rߖ7 b ۏ˯P8vH`O$S8GjSK%4E]4(E7 ڈso>'|SzG_ښ+[Tr 0$qǫϬݗ܅@>6>AuuI&7<=p~'׆Zͬn>ku])18w*JƤf ߟ֋Wҧߺ^׽u~{ߺ^D"GujT3y&q^ǿpu IŦZ~=__Qua 4 jPߞ=i]rLI␱>YO!@ o^={=sLE'׶X߇=x)NÎB_I#=<*U }Axhܪܵ ߸ōK=n.cB?}_X8}ׄ8D  Fso?ݺzAXo{A"/n/?H]rZ L,Tzy{GWM cUO͔(9f u>Zu=3Isb@(7v߉oKuMύ16Kk~Hof>mc>Qʑ6)H1={OX[xxTO^|IonO߿W={I>]>壨 $1QfbH:l~_^Nc[ȸ[Ͽu&#r{G(Fq[N9'OzzZ>?D[[6Ͻj_ϫS܅($N{ïh^6 N~>p;޷0Cb{ZAXY2E^?1}}֬8 ^j/{Ky~]x.1ltX06êRzX#77(_W,LtnmuM#zZ<\;ԄabX[]~߸%To:S#so}`gH_+yJF~=\Ư]k*[=בֿG{Z>o~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~choԊy#{z}S[}~߫z`)Hb>O~c/{=oO}zO}گb/a{:G\5$f駛ϿPuORȸ ck"ډcEʼn:zݯk}}׳(ڇTz%mZ? }>A[z7n=u졸B{[^(ֺP oTu믳S,m?"_ϿSW,o{ֺnj(uf@Icʥ<G^uP\2}?H/{UzzhiJ < [78u*7t`mŚ?kD@ۀ>^?>IIM#f7J@ߩ^Zu쨸p g6_Q/Tq`ֺ=Ia@D!@$ ߟ~f}pi[nISp@# /skctْ_WG~C?\L"H6?ׂ}lBGpOV3~JCuөNȪK :cq@5tD\^?Oު8u7W㐀;~rFz,bmUO}Tu_qdqouMSʾ #Pm]c;4* lX-o~+M8Clx/[ӏ^慕Br.T˦߸u)Ys]{XNjI'[ :we*&% ֈpXGfIRIkngtnlRxbpP&ޛzqq}$?Mizrc#^oW5k?ޖZ¾*_,?kJzuWV %z'i_1Ժ*jQ~@ RH-k #| $/Yn T䋂o8?1dƹ}#ݫ'JiZ@%v ?mc^]\qX[EfVsϐ8$ϭW׮zS?K^\QϽk(/U~p^} E_\hrFeA᎛?KYk48e#m^#E..O7ou\/<>:~P>ǪxW񋺎P[~ߺS£,>AgKcc,Wƫ{\5zW'>So~N Z]Nz޺ߧR}kϩ#ߺT_׏N {^7uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׉l?ߺ]\uDh.K*~^M:Ƶ '$ [\ǯu/޺Pф,V:E^b5h/Á='o6zrJ&kؐ?_߫=:tc`Oֺgj_X,E, mU:}Gȸ[zo] ?ur2uC]߲< GꐏO+֨}a"BlKiokߒ?[O[iԊZPU,Uؓn?OJ o"^ߺH2jAk [p?I tQd8rnH߁$g/W_0& xX|ˤJW[Jd U&{`⊌y$^uHnn Tqrlmc?uY3.lu:|XT5_/dGUoXrMfxOJSWLeeY=EH,s}X%ßk^>c cq ~>@ʓuVQ7YKPT}֝yI=*[Ө(bxt>8z!~ӎ1oVb}ZPt6a Ob>{Эz|~&ca?Rl=u4z*3j1ǭ֦KlJeZӵx/~>s0@#+_-z3ӧ??Ǜ-}xp<IF˦+ƒ}Ԏ?z3{w^(O~Ǭ_~VDF΄}G6y?Ghq!6kߎ XquǤTn=q8=zu63׏R]hHԨ>׏SߺY}uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^L.n@u@"\׺n-$ soeh:PԟAչPWqc~n#qJ*K[YEu8GQqkZߺ]q?UdtlO{Vt}S$}OXŹ]o#[H_x}޾g}{`x?k?{|8u/?G׼wr}˪t#% Tǿ.Ix&O=yi _QP ݳANQw3L@(t8W_˫t6n߽z!%_[r>DO{lϯNiWR,nÒ/>_iW $XH?{zi?trEO?$pA9WgU_g&_{}}=6xmu\!@7GofW#P&ŀٲo5<ްz a]i?-Zzu@<_t%Ii&\z\aŠakYx,E?7WJҿ6qBnl"ܛ?^8c 6(Ati?c;F'UzV` ]S_SRI=,\2opEghq$ON@Q#ۆPTr~]_4Xl&Gx)ױB/</{Z㞹!өm {˭&X_?m}4|H,yHt>:_|Asaq8W^#׉=r^_>B ~O}>JJ硃?SG-5Îuo>q"?k4zpuċr jzuk/I{pNlH@?߫׏BD/Ź[^oUcϽ^׽u~{ߺ^)A}ֿ5oqY?׆ѸZt_/ɱ#uV/s\:{=b?#ߩ׼Sm伡UEƬOoӯ= o@HHn=M=[G-efepokN2kO~?>_^zY꣍zӫt"]MCCnBE,, {Q rS3 AuJ }o~GǿuǮ~}|Vwqs|5V:yrGuiM(z0T[iÇTqI<\{^@dV'O^)-?OxXno>W׷:Y>>>޲ ^'ޏbj:L@+MW=k4[ą 5(=V?kq}W o /{cHظ A7ןzNtjOFÏ?O~éoOԤ{ԥh.+ߺ?~]eߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~AB ?{z ߌO9 9 SWoZ#ߏ^_Q_- X{Xص&,=q8$ :qov4M ~:OSco|zSq$puo.+NM,I7GN[ŀ!}"so?Ou"\``ҝc Msrmz['#_oUjĎx?>]l|=IPSkSŸݔPZk?~/ÇM0zGd@5W $|}ѰkNOOqWWPI_Az9P㮽?@@7< ?yO_`v 5:F# d^WGې?ۓ'݇T`|Og-`$jNsW˦1nI66 tZըAg3s,S{F7 yx׎>܀.?G^JNpH?C?K$t_qrO^^INb829&${o!2mׇ}O矧7 ?uCy`oԛ|׼mҟ/pi=6kQmG~ udWHRʺC-9\|_]pO'z^'^2*<)o{װst{* !`5_~G[` %c^8_v4i'tŵ0<c} O|JX  }5uW뙝/o]sq{4k~lZOn)8PG/Oz&垅.r9#J{kk}k{^׺u{{^5Kqco~]z5 Xԛm:ux~}'L 96^_ϺS| 7?߾g^iBX{:]j[E^Jݻ!f[jHoM66s#g G!7O=&ǟ{WqTOU1"5 @r~޸=9Pxtɜ}6fGZq9efhߪHz|SsoǽfgOݵa8H5[8tU6G{'qзc$`c>7zvo֗'l |!* #N<܁m#==9#o O&[}:.ݭaco g:vd6i?O@ídMVouu ^Gߪ hӕ&٪A#+_ARo>{uGBh[h}Lop-<x\ ` ]Anmyg[r[Rcpl-c@cyu%l?K8_ߴׯ=rZ@ }H?o^5y@~n~Eϖzʻ8\}E~ yWUE:6kv?{YU>1HK%#쏯{Ov:} P@'?Z鎣$&n~{|}:edM?~d%0~qߺ=g\I_?_k޺zθJ׫e?/u @ Oue$~s?׺O~w{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׍xGW'${^4)е~{=x2Q$GfV` ~ '߉޺M" "c"J8u臋~S:i"[{p?߀=jz <ʁ[rEΫ_LkëtRHsDWN2PHȑWC) ɽMr}z ͤÐʛS Mߴ>Iޫ B9$}xO uc_H4s |o>U'zR Qk^'ݼzM'fdYH}$^ZsI@ 5iN 0}'A[[~9߸_U˷A2C)1߸ucS҂^c͂"QoI}u~{ߺ^׽u~t0 uR!զX7}zˬgmЛp޺\7'J~O<_{:M)A5Qz" _\o볁ʑI7OX=^Cc@?{xxʚ4}kLScz^ImB.C/^c, `r> \ ϻ RxzF㽁Qے/^zHIB@FB [}z׭0  oduA8҉J"&YXnH>֍8tb^7okoG[Z^{^ži! CcdQnq4Wߺ^H_~{sO%}u׺ߺ^A׽~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~eŔꎸ[?K={h}x_~e&ܞSTclxu﹃:/uu﹃:-_~Zϧ\x ?Ks{.Mu9?퇿u?>^CJSX_z^zp!I8KR}u~{ߺ^׽u"7~7I(7 ܟ`}㭁^_?Tubx'߉i=tsCkO~[_|:qu5r P?E<`O{БQ_\re r> + g Qsp-.I,u}״TnZ#?WSX*[~WM}=˯Sΰ&K6ֺa;5+?kNC=/]>AN7$M{iZu??~θz`ԫ}L`Y޽={N+^TBCJ {ZO/8]Տ6EO~&oMxwDm9ÞW˭dq=cmރ'T!K}lnj{OϬGy76s{^D {׊:yeX=,n>?S>Pz1^:H^qǽR).g8q*7P>\}螬WL?)x>oki^nHZޑ_ Gb zu{{^׺u{{^׺u{{^׺Zb"3U~[+]YHx>Gt&AN/7\{WՈzr7[==T:rUnj'q~{w[нxWe`=@u-eTKήlC _~=֦WZ\΢J؀mm H½oc_:ίD.ac?W_yTZMht?JًG =k_KUߓ~I}=l,?؃ǻpꇏIl,A ?^}:4n 1AvU:0Е5u E!Qq,Dd,>݇CL^]1MȵO۔^ KMZByyZ+NxFTڙE8ï=vO3Z$:<^8`3ǩIگ`~Cޏ׏B A$xro_~oUn'L~ڋ mD5ounkJeV+yr:\LIk[xǛ5(O_ߛzǬ@ ! x?~o{zзA<~v:h+ui $ak׏z[:|^I8 ۃMXpQo<mǿ}xkrm{?޸uS&..n=Sԟ ޽~߃n>OV\o`M[s|\oWX?G>~yuOӥ_7 ^zJN?^XI5j66?pW}TmIV#ubYe=< }3Q^׽u~{ߺ^ y`H \ o?>i_=F%n8nMɲبЎ>tku\}^nG }VVF=<:*)?zX꽬s{y{ۂ>,@ntq/5i,o㹹}N8t'J i?>o?]L@UR 􂤑b?cR??c#kkyso> ^jlxE#o~ǥ`H征0I{R1חH B>_Ozsk}mup뻞6 O?>׾G+sc^:DH!A\^AaæϤV뷝~@^=:I}~y#sk@_?Kx_޸^T^fYC_ۣ^u5~8X?ѧ[<:6[zb##@S_Z*u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽tݒm0;rt!xgկ~ڋsT}JtӦ{n,.yuAǮV뚏~z/Z[~Ͻm@cVju~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^$)"ۋ 8)U7M#q{Sz[E/Ӫv.-I&OOW~]1TO ?}&}~xaakȽ:ӟgc\_zѠOo?}k>"?{V'Ӯ͏'7>m~}i}}j?>_~L;Ijԏ{äo\Ue#ſ=uX_RHk7Z7?7CQ\Or.l z@mo~:n6$iBo=+u~{ߺ^׽u\ZmGy`*H!IH:.~O-ӃQjSs˂-n}'=X<QOI(!?OBO$,82aW~}[rE _MoʂnBb?ԟߺY3ӟ{Doo Iy<#݇M+sK-(ո׫?,?A9Տ]#ݿIGu/MM9Af,ސm`k^גdSuIe#Mb?NO wM?c:m׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^^X^@u߾ζxtf*e0сR܍_Я&{gӫǦo\9Pu'׭qYV8O˭kîGޝ<:߮:1k?}W{ϯU5=aU &ĉ$m'#W]xӡwf?"_/ct{{^׺u{{^׺u{{^׺u{{^׺@e[{^A>׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~qeVe ?{"FXsuT'u<,jXMhxC{ {ǧ[:1ܐHޱ׫Q$5'ހDJa >^'7o}uС~ ^NMzG'n-ȷߩ׫86 ~?$}xSF$P Ao4BXIuﲏ͸n/!{:zbiIO~:kǮֆ4 }Uk}kʃ׺u{{^׺"kR ے/kǿuG#1.Q$[~zX?LK{S#ek@o޽jn8?GϽPutMNɡI77q`7{9qq#H_XĀ,"umGˬcu?kQ^^p>NSJu/Ek}WөPcc\* ^}8Ԫt.skryS ??Tu]w/ȷ>N_]w?9_ǿP.҂4]Mx#VB={W˧zzdBǀ,HT_{DZo![آznLf[=nv65_xcK{ؠ\nfhm}j׮KFMi/ž|^=;Ct1aO$N2 H `T7~ՠ?j{ܘ>X>OA ?ϯ -8„>]oQ鹰r%35\4o~\>`|Z9-p?*}jjc==QRLqn(OO_m"'`?<4g_Z<_n>ߪ:Ӯ{:ӮuIQ {.B Y}uѿuwq_O~^yP?Oǿcwzuמ?Tu7 u<mo~Z}rͿ>QS~E{G,OF O׍O_]^;ۛsQ,3^}\_CߩSu׾Ɩ0?Kj[ߩ[>T  q{[޿ϬQUĬHn~h瞜XO~Mooo~]vҢpO?{^G&}uѨ@?}}tjRTZߓ|^zWP}t믹_kR?U={\#^7ֺXc{/"'m<^ߺQ+s,x6[߸u)Ǯ?sK%ߺ]}?7 OuW미so~rS$\~:5ߟ{=tByW`O"^NF-ߟ~e׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^m*Oɷu@Qp*-߆z49^/dS[^ZմЏzӇutF=,Ûq{}/ꎷCǬ~>#UkOϏ\>m+c߳NiHjyIR M>߳׍ΕfM{9$_}oz[+LY?_Xzz~U' ӯi=dgU5 y"TS$h,&=eV FN*x$X~֝{]?>AT(?^RdFG]67`Ak^ǝ?#ޚ^ u+Q,VOC=Z2tM4o o*P+y:y [ЀojAVUOQW)X-c}-s[׫W#ʸ]_S{u>Gitr=Dp+5~}ꧭb:WEw7`\t؞=ߪRN׽u~{ߺ^׽u~{ߺ^׽u~$7J * {Y@#=$=6!;8 ߺWp:7SqFX~˭O!SI}?4Ms 1e: E B>:W-LAP? \~?{WpdXx{nY _׽IqgkއVר7> uj ~V8S)rT:( ~um#C S,LMlIm>aBGM{$2i:ĐqgA>@"ܛO~Sӫ]}Oy$s˭ҝp3 4QM'Aּdv&ޖ}6M>xyYJG,ӟuЏ4Ek Pꀀ}T 35IYVsFf6z&ַjGZ:s# Z=r1P6 etғZX>z̵t5#E<W=2k%m?rߓcyp:2K>?޿ǟ~dxs%l?W4{Y[ mS'}@)VA% _u㎅ ]s-#{L;jzr"b= A`V0\yvn$ :2(d~ν@:uK7*q֨=:ueu.rxo T8c+꥝vW7?SYzB)\ '_ISv?×)OAHSub ؃ ֺGϠM(]͈<&ìyS_J_t뿻c+}j66]e\opl o>kh:ȵQ/7?}ltji7ې.?֚B9ݝ16,>n<+҃޺^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ ?Cso=t-#S\qhӁ~yZ7ה]~VdZ_CW*o,O?6߃o{Z>7R}zf'-<~Ox4OSZ~HGOא X[zcӯr? {={-=р`?鞼x^j?R^Ejp[\n7w3uHmp{E\Ꞹп@mpͯ??o~xuu7Ivk[.\kujJZ}(G޺HG es{p@6AbrWˮГMC.1Wθ$((O * }~3ղNm}>qOj՞nԏO>M8uA􌫿 _F?uq\87Q A*H<6ZWV<~.}GX'@m:(3:ăflM?{Ztśǁ#7ӟ{{^׺u{{^׺u{{^/:8'~}Ն:toYnA?Q{[t/{qa8Skз JO7{ꇤ>$HߋX}?Ͻ0qV3KZSqɷ_uujz߆u=1LךBG1$Ƿ1ףX!q/ׁ4cruooǽE6թutZ"ލ)[3[XȵXbup9xk{8u.?$ߺN;K%n/C^}Uz+6m!{LJTzȰ# "ߍ$?GA4ǦoOu:]~qև^guRB.>Z[t\pMKs^'4b=#=ԚRs-cGN=2 }ۯϫuz5:'WA6_ށ%jzfOv0_WvsEA4>:s~u={~z{-|q}[]^Ž+&_tp>k/;p@g ?տCӣ][v4(֎YEoUzʃ'߇[<㎸a`x6o=\zT-o׋Oϩ40`Z:RVwYBB ]Zq=:{c?g<<>*AZwdI.V?JHn@߆*Iƒ_M"8(ܑ.}q\:p#o {鑞gX}G>p7$\qǧ{+`,n,`:*-$)Xouf:e77"x畹$Pm?ǿp@We<[}ߦNֺu{{^׺u{{^׺u{&轔GpeWH_E& rO_>޽N}\]xZ܋[r}ΏGo67{k׏zDu$t?њæHr>G*I o=ǫ/N+_oc{^=_e'n@yS)J?>F<ۀcfz#i#]>r8WONL)]< @MMze iח}lp'o7Wwyۑ7_>n[\7# E4T_z2_ {:hzm~/^oXFۃz*zO~}rW߱íǽuç -}a'ʞ$g4~KuqO % O"@9ԯ{^׽}{J8'zD& ַ߸u>0#\mp[= ~opd 9kxCִult:ctԿSon}h?޸uQǬuǧHr7=uB4q~yoŭݺk˥u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^9SȌ+bZ?. / {8_}?tu뤣0,W $~>}jӯuæi&yYƒ9yk^ N9S?OHxk~A{[ ,0H&BP Z'ϭTzu{p>M׷um$A?sujׯ܎GOS~8uэ%kp=^u @"{zFxR*?؟,G>z4=&$?v:\Mav6Uō[ߵ={Oc7k3ϿRifG.c]@^ֺu{{^׺u{{^׺u{>iFDI ?~}Wϫh O29_M9x]=X?g_k߃~-uE_o6\޺sz:*ԪڝR?^BOZ'Qł) eUrO_{Pҽ#7-=D$.tٸkr??>rzu 57?>x_i]q QzצLG&[˭u( `ܰ$XS׸t.v[F_?[8?@١t$NR5I`:@[>E?A sƟW$yuQՎL?ܐ}ȎH<:}|,?`:Uir>Sƣt쓎QZj~+~5u:1N {Z$u쪅kؕӟuiׁ<`Hn7 okiMxt0c<?݇t1,:QC1znNS{}8iGVO G|ӕ}f\maqu<:mU/އ=U#s~]jQx-_>4 PŁѩ  R??_ݺh7bX16[~~N}k‡|Ŵ?skOCR<8uLߕs#o? j+ױFk}?v!a8 ߡ߫׎ONبt0 ,-kM3כs?ſ'Mt׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~:ߺA뇚;PCk{[Ozc2 X\X~׸מ/_~qǿu5d H%//uu*v ?6{ޫשrBOIS[bkAAz[z𮀋݇_^?~Ouu*nv^^CJ(,~&Qlu߽Wӯ}< OשyWU]sJk?_\ӯgAfq?S`=q 0'9 ZuШ#ԑ͉<{ƽwcԼ͡s@F~޻I B"U"r ~[k2,Mx'ֺ$hx47߱תzhm|ߩ֪O]}'ƜrO~N'%O{"=L4ml~ì׺u{{^׺Ψ.?׾]`x}}>뿺QgӯcׯEǟx뿹Qu^$ pE׺+@*3>B?^X<ԟс=t\թM1~08Km8Z'Ϗ\ɯm^14Z87GuCT(Wޑ`?<uֱu徾׍iHP_^I-<{zP:n -o~"VO1=$΢o~˯>D~ާ%h8|Q|8pn&Z:﷢r[?>L]^ok_ީ׫^VKrI?_{^$tJ6$X߅8ZS' UVޫ-}ko%&__[뿰N{"={]O#t"Au{nAZڀ_ŅUu4uВ9Ш{˷zqO [=ւZP onyQժG\QrKA&<Ӫ=yqkbXA{ؒo{^85}TPu[yE߸u]̽zէ_] 7IS׿\DlooOϽuk׿PoG.-ǽPbPs">׳׆2 O?Ӥ/#@^RE=rX}Hߩ^+`hXRl r {[8B?ϯdS u=6ʽz׎ KrMz^ ~޻I{#^?>T Xtt MBG|zuy+6k}^Z׺u{s{u~{{^^:c~uiy׽_?>׺{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^*DKo̊?>zM6-s&Vî7s׏{^]v2[Ԡ{rOu?n8翚O-8  ǿ8uGБk}zY`;݀~k׉4Oo?~?z`\_\}֝_][r9$o_{˯W6Iy@E?'ӓo^T? ʯ]\'ߺ&dC66mމ4-//$17! H o֨+UWzC,.$,A]`T m"}z9gh%Yn/͏_]x  o'oʓǦN>׺u{{^׺iHұVlM ~߱Uo77~˫ڠdǽ׎M)ׅmY9W [ިx@sZʲяsvPy<ވu^K6,2|^#>GI$C]‚Tj:G=UaN}nXX~8?꧇뀫JO AϿu&y$^yzY+ d`!]kf+oOX>jt.$vT[Q~WuCF訖nlAbU \6#/n ˂I*GYZˬ?|z؟>O*W [(ku[_#WP %%[{Zfsr7ek ȵH#0A~}ՉNx'ؑpϺ4㞷}:zb'?Kq{=[u+^3ʏoޢzޕ]6fmy9,E 7x$*z[25.љeg{I,ϻDFRD׿|ldB G, .O6bEM=r3Qxɽ16؟ϽWӫt7N@Ss{j$ qNS4e#-puI`>S={Nz\A^qz+ANy9OqT:^\Ik}L<$z}8=drЏOZ+ЋyH盏lYJ$Eok_~5˭}z e|si a s]tNpd{ɧߵu]o?~O״ [rLC~,G?>E1/mE'ֵ>-˧ WQ/7`-p/`G'ޫ^+O:iJUlmoN($[{Z1Wx/h,yӏV;VZ\_޺:<}=jH\1=S ?]?}zg&?ٷj '?=SӬ>שԈ->ֺ׺ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^'?W<~MucŁ^׹HO _o~_Cߺϥ2N-4}8}u ~ߺ:.97]xӐ&CpyXu¼z{_z`&ǟ^z'Q#Oyun?Y`?}D.>8çZ-?}xqY&=߇huİ x{~}c@ % _,>a KXx{ֹWW5/J!7 non4N ͩkj}E1N*sckj> &{Q{K16z~}{k57kf}{gK)o9?wꂀg"kuy؂-bF3ì !j݉-U^?ge-pŹ??^~߾gPW_ˬ(%%OŸK~?HS^WHnmm=]x[GBMu7psdE~8ӭ~Yc&_K^}6i^[(~Ͻ7Ӥ<"ߟxazA$_>z:c,P Q|}+@zfCp?8~&z 1+}nS{X^Nۋ O{^2$XZַƵq?׽筏C׸'sv^u#n6u:ϥN2}9`y Y'UsЯ=<#L_\~=Pzq'{cӉuאM{uynq{N~?/~aG{.6: q[Cxv^(iשtf*Hcm^oZjI5=U Ө pBMw鱓__UKQ [(ok{Rũ\u1RM껋~XSӭҢaYɰP^ s['_o\BX 4W#͏z_<ߛb/T=9{Z׺u{{^,A@ar׽lNz Y>Mgov5CC}=hW?" ~޴:aCҷޏ|q><~??__7N=v &r>G^"wmoOt\zHI [~zB0  N>Jt/u72 ƹVB/зOxÇ^^o6{Z={c{S KHp=@=y ɡ?@St g#~s{tjTOMdoǽ V[ˮË?sϿg|}(:Ϭ3~_Kyۊ<}8z= _yv'rZPxOu^uc]}fQxꃏ\<{Mqպy_GkN 86{rY,~5?>xֹ!9)rO-H:x7GKk~^xu߳zMcǯkpkq^“d17-\P@R/5x3:ԓǶ˧|X}u~׏,?޾=_s޺8~<:N#އZn`??o>>}< y>H<i$,8ߺ閟އVӂ}Ͻ^'uǩI׽zH_uì~M~?.?~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽RzUOT}=u׍ ׸] {?~bh!svIcR:}<ߩת|~Eqb-oSպȳV'KZ8kj{{ϯT҇F.~:>;ԯgur[_z[^.COkֱiSҼkk~9cK؟~u1,?.M(#k7^Qnk=ˇ^$ֵH=PVNjc>Tzu~}rc` H{^,xq|AUGA?Qi C^#{F:gQH9+E7{֐zo_ޒ& ͔~??_"?G?D<j[?@5/[.(h`0C 7IS}u~{ߺ^׽u n-#ׅ߱kͷn|LMMP.G\KA8qߨzޥO^ls_/'~?'x׬'okU[cn4aԊ,qOPA1Z9M9sը0ŞO& ߈Y붛o \I'- Tӏ:aƇ!$$zz֯^U(gnYz14B1 lnoT7qVWRN?_zaAQ߈K{ӂ: U^ypyOZ,+U$ەd IP?ײF?XS Z\^&>[p pA\ƽuҞ] }/d{` -è{IhS(>sk_sD=xgªԊٚR9Q^1Ud*p9%[~+M#r8z5i~OԜ^ [ ёԞObƟP:LVX0xsНb԰ xuC:j]≝ I[pEk=Շ8쫓[_I>kհ:SV~b}G^KZn @>z@7:n<}=zY<(@W}{U''TT(7p=7^?{pd}JYє) \/rsnmsJcLKS`<\o=w *mcUӯTp)Ѓ~B떖i?IP:zZ%m@^޼OC>0%GݼH=}@,ak)Ͻ.O@zІ X/i?.o>_#ֳ R޿Ï^u'-'Sר=z-?]zzȋ}A 7ME^= P[ݺd׬??狟[= q$O͹:8uZ_gVtu*nVzzO']X uo~=hR:cm1s-{}u~GB3Q\#{鳞?|z 7q>:fkC:SsU=NC~}LOz>׼_~߇ZM{ZtC{׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~x >=u~EU*sͿqzu׸bG^^!m?~]l  QשӲHmvU<}9֏嫑%dn7y^}\XXׂ??~]Zd^ߧuZ$߃kEͿ߫J~Ͻ{o!I-U5Ep}׾K[~l8OuuUһn9:@Z^NsLK0>eљ}DD#V䏯{z?o]6ۂ<@OʑLӯ\TQnloR|Iju F׵~AhӼx-_kܙX$%&ᄒSZu`WRldžR@CkN}Viu켏?NִΟ5$x4aavsi׊(<$} }$~?#L5Yy._z$Xl4}B렀A~=6*zޜW<řt dר)Cp+O$:Miߏ~ՎJQ_ϻuN7V֒@C{@^Xן_~ [ NzZ7=oCRW볟JP}DP/soߪ+N_"cTtK p>^UN5#.̀SO~:=0F G<~?>z]G.POzkKΐd[`?k߿.V=SԥH=*}/jI׽u~{ߺ^׽u#KG$%~^^~^ ½D\)j o!@O)g/~Zh{s=꣯À fp=)6b+\ad\$5Fn~Y$?_jh)_"W}G]ULnX$Vo~}6IT`WQ$5޵W\HA F~? j 8A2$ V @pNz vsh$4cWIGߩS(L.@mbyu+ӱ1$xʐB?{^MCHad73%xGVҾ]b4T.[3ׅ-Ⱥi6(Jpuѥ^<s7u#4W둡n9\ ? &dd*mdXckY/ݎz ƎXZ޺Mo,_~8yyZ}p8}?oV:֒EyN rT׽ZZ <*/?}ǿׇUT8s}kn4ԮͿ?SR'cazjJ]mr[ߩ׫ԅƲlGuhXh~^Y suS8Ͻ@}ą~?~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~qoS{^׺_c^#_5պkBnO$>׫Cׅ',G{ ~ޕqݺLu.o{ k _[ #?C]s{[?^Yc9^}_5xc?CIzm~պ6<~xÇ[GYbmtSa{U="S*NMk}GV<*zQS"ؐFI_߁~"SZ@6X_Mʰ{Q>,Ӷ(G}G?ֈҢm f u n>TN_Y끲aOExSWŃoO[߈Pyu\:I(k8pG*nkM8_t?\~yo.WH쁵DMX {V8^tM)?->usPp1D \V%ʯP?Ïz:ǥDEIDoׁ~?^/qDak#Tn٫ymQ1+d ?}=Zg(ʰ e W'H&_׈q-^!}D ?#}jL$ ?}?qYJݽb^䋮߼~]k?.9'lȵG I-ϭž6{z\>5ͬ/to{P?ǃߨ^ڪv(?uz v'馭3u{{^׺u{u W{r8HP"+׫5ŭ_}t( \@s{1={:YӛmiOBw)-A$[W$}?Ouv-1(ji[n5[ߍi^ ]I-s+plsbOW W\ie'@?>{\L6uƒƮ.nI8#^J Q֜:\dY2-`ŇߵPҷoU@GϿ9"NAa{#?LtSbO}n:CcbZCo&X`Op["` kx6.lvk~{bGI":X؎M+NXLEhSi68u=qҖO<ϭ'HI!r$avOɹ_8_Q< _)oz$xS׏]~VH7ڸ?C{uM%?T =Dk^V)N/LGc8R) >7&qun8Tuq{_@8<܋~GӇ<yuȐMʑn, -s/U냒-E$#[{unvM Xy_>8tWVæ<"G77X#Aqzyu:i8AmI??j?9xdMP!lAq9xztH8UW M ,8)֎IgHaaUOu: :o]O߳\u~ x6ԯ^]y$͈ߏ~u~ ގ)^= jU;}.}&?:m׽u~{ߺ^׽t\D~\uu3CrMoYx6[8~ײ:cZir_nuXW \޼։BcGBnG{:]nJ%]Ϥݾz>_iC)M<sGm[o{[u省O*u7c8^:Pmv@ 1n[ێ=Ҥ} qDT os^b@ⱸKdgSIB>_u+O~3#Oֺ߳(%}"^ǿ}{O6O!د $؏ӁծU<1O?5o~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^Mh<𿋟z1[VKar.IǧAXJUW׫~4'ߪ =5[m_ʊcp5O6:}˯=L&'n-P֏B(kd=Au>W/6ak\%ֲ^y8j6Ͻ}[_}u>6{lm'u3Nǫz_.+?}kΝgVo{qԈ؛su!O~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u_~q1f?>׺8=u2I"G[A/oR\r֚\Ũ%K-<߿uNRESօj)6jZ#ڤoԿ؎}0b4VP\ ,$K}nu՝'@r-b?߃%ztᎆX]̪Y\pnn. {\:ҒYl/ͭ~>t T,Xo~=ֵ=T ]&k^?Ccouz U  OoeZZynC*T)dHZvWݨk֋)tJXs#~|Gdi%Ьl-!oy+W2nrV kՅ+)1.nKs`m_ݰ^ly|]Z龰ß{ u\_aGzmkX}p?.mI*_H9un uu8=0p5\B, ou8W_Xeym`{q׎): qk>~}ܕ׆:Tmr*1[^gUn9J8'ꋚ}r%\\m>]-&cc ZKPZϗ])7eWJHXRr~{ PJN/{{r?o4}QzR{Z׺u{{^׺AnhCQ͍£_>=!t P>GzY.PӒ?íփIS>~/pT1uqs>OZ5ЩO+ y?=qqRw>=x<>=u.Ў޽}=m ~EGC-57[xA&>X?޽տӪ:K/xߧ<թ1߳ǭu~ߩױ9վH:J<_0 ac?˪>/ sRu~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺMu9G@ua5 WtcӋ]=7__uG׮~׺fc}^Ux?}DPs_ߏ\z1iksa!fzXͿ'ߺIlnXtz>x61^ּ_z~}JQ8ש 98ֺ}2?O~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~tYGԁߺ]^$CSM~y\xNPA"o߱׳׍T߸9{>g}ߺXxA/Ͽu?ߪ:}:k z?'T]?ȷϿWii%.ߏ>שO.6ozPHfrA߉^5(&>-O~= iXدqXd8FE꟝V[\_{Gf`Hk7[s~gPTD_IGcي?/}{=u⣽o{: :-'zP4[>U33 >epmT(:4gbx3^uWWb-nc}'XZA8?1Bnܵ=6׾ްQCHtU+ί ۟gKM}W/Zl5+AǀG{׵zZkZ[{Z$Xx`N_N&gB9}:gl;KW` ?T>b;nFԃrܟuGϮ'nubZߩ׫_=0.> /fOzzچы 4UQ{zsON׺u{{^׺uL(-}_ԛp~F(:#Ϥٲ؏7oz2%U A*O^Qæv*]m?ROЎ?{W 1%/X{z֮8<|P!IR$C{k[u35K$ѴHHTZ'߈ZSA~Z{'cnmaoz٩V ԀPuq_Q 7<Zzro:GӆQIP\H`xSƒUV47< } ~8# EMMCo TZtj tXH$Qc?[8q9O)\$ZUֵ/^KŁS7stn'_^fIu+Ӱno{{V<^8ק4h rn8k>O~PGZ9V:(ibm<_k^׺u{{^׺u{{^׺u{{^׺u{~~Mue4!hrM'pMzpySxϠ_yZӯ=s^@_>cEozlשu*!SǃCBf &|Mmo~ꧏIrȰ^^-+}=䎧?k??ߺנjm~UJ~RP~}yu~u!#׼~^]dߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^D/{!Jy׺"*nIV_~gOПUzGG_#ޏ[6uֵɹ}o{ծ:\BzOO(v関i^>]aV?l}xۛqӯSz׍^A>l?}=G~=Atb9a_ޏ˫`<[aŬ?׺&}Ͻ|z+XC `-׳çڎ r>E=QJNU&GcdƮ?T_N3鼀ؕ7חTzAϸ*ViDV#ߪ8u` z護X8WgiUp l?ǓmG_zVYtLj ڔ/ yt׽Y]Mj`C?}=qOY-W߉Zː!m7MHmڅ+Oմd..@(\rZy{`OYPCOjoZP`Pʤݘ~"=o@J*ūRܫ6:Mu!6yj6&ױ>ր'Ms2'Oo<{@m'Ϭsfo(_6ߋ!֊5xӨrP# W_Tm^]x<T$uڏ' Q7 B~_z.E:vD` ?C{~_[O{[ ??!ֺ/O~u8R[{[/uN|%.z:9©yu{=h)˯x ,q`| z\OSUX5+ ܱ+#x_~G߿}{<1(i}Q[]~~ NUUo}uі1ۃ~uZ/׼N>~={:K{^i{^׫׽u~{ߺ^׽u~{ߺ^׽u*U珯z=lg/fq\mWq9aDT)LBlIq~OS|vEƝD}n@\}j c+|@ XTےOÏJuU˄SyudLEx6r8exŅ:\#!oNjlO{-/}^ab@"RI7=˭ix?~.ء}T>׺m}zKE`~W˩+zOud׼Ο{ZJ}O׺{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~G~.9u?BWߺP??_ߏׇMB- -n??{zC`܋ſ?==hunHcϦׯcͧҼN==2ԛ:ENzZSco_׳׮>ߖ{={<:R,A_?=c[q|dpu=Qp^^@{ޭgyu+hO7#ߺMK}tä;}]w*1-p n,uj 2k { ܝ@#Fx<ӯG[O>p_{o͏dM:s^Rm~1JW 8ߋȰZ*[ ׬]i~$H (MGaHnGϦJe,*=O?\s?Qq>e C}Rx _.Tw\9? ׏@:[a?͵Anq:S?gyKpH@gI̋`Mh~O~=zpyS^zT7K6r?Z'_;y\~H vj}>dd*6A-kiGl8k!?GUqKV L-bl ~?>8</>N={Yֹ޳׳׿x_Mqzm|Dx_pO#=:zԵ-?A* Cs`/W)N;ON6XGo~˯x \ߺ82*+x먑߾޽) |Q>fۃ6c_c{u8Yk>Z?pe7:mJ?NSf$ \\bI[:f@@./#pyѰzHxm"m^!_@<y|}Ϫ7^,?y$M>c][׺u{{^C\k;Sf6) jI HMZW:פDuE}6߂9&zYG&JnyzmM½x볔ɭW#r/=Q^zufr<鞫kp/y tVI39 SsNTTt-I$ZۂHL=!w&ZJar!EņI:~Mވ# ~ޑ̠_TꊟoCZ~}^?gRxd͋?M=45ZiԔyzOX#Ks_ezzλַQx* [u>=Hz3xn [mڇ u'ϥ7#%@R5c#Mi҇^׽u~{ߺ^׽u~pw ~׺eR@Ƽ@qnE\+l}꾽z"eR\]-ͿïgEBO7'ue0E Sr}/}d 7q?7Q֨/ n8O{,Pт}n>$[[-[ߺX$@nߟ׺>.9<@?o{_a{^뿲A?{^v)O׺s{b?׺~2>a{/~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u6b/{R ~g^ Zq8{^Ao܁~<:_ nOLJ^ؐAqT^a6<y?{^<3҂#xZ8<{5GJo{;-l=uߍ qO]oˮֽŸ[׫Ǯڒ'?׳f_ڿkΝ15:~ln?S[pG['r. =zj^=k{qȽ&K׺zBVR>˯fRyq`n?}knG}tդ]ΐ ߋxq=x~ޡI@6PNǽå .NH^x_ׅGjbB\rA kr?>zָ=NEf+uRy~-JW_Vzr_]a'ԥH<_~_lԚt(#?7oo `8<:xŹk~- ׈K4o ?׵ꃏA=ue@{[l:= ~V]Coݺׅ7cz+g\qs&:Amj p@O?)^jWpRx"~_S׆qln[{Al9܀>}ǿSpa罯{#1Q׺$[v zCEqr:UC"{ߺ^׽u~{ߺ^OW\8<A&W&N!Abo{ιX/{~l?S׭}-o~1־Μq$uI$^Ǔt^#ӡo ,IǷk:wlHʰ}u>ē4yzpu}zf^m5{[H"{˭ @@R: 8u}S{^׺u{{^׺uUp8ǯѩgnT=ոg|fCr@\u:*?oſ}=C߻!?Ǐ]HZʥY {Z&ILod<쎼@r<Oǿf{MOK<%l":H6A{Lo?7qWϩ׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^D,@#}uBNO ~9ש?ߎzOY>ߺXdc~pX?{>p(.M\ި:zP?}5H?q}zKkODڃبn~Pyu}zq݌-ON^'{}zˮKD6/ߩ֪kPWOXg={ƣ$)? iM$ Ji* >c= νtpգΟ^@>c1Ib.dDSj;nы} pV<3_UCTgf mk~7lӹ:q^qq mG U\'Vzԛ&I׫tzҒ $EAr~ >s^ZEQv0'^,)7:T^CŬN?g\-8K?%?׷u>KۋOp.rmA<vx&mƺ pX>8g7^ȱO0zWAFr A!A7ootNˮ.?i׎:kM Mǽáu?xJط?<{z@}ltԿRn?O"~]x|Σz[ߺH68ׁ_{xoߋO~ꧡ22?=Ru~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^GUŴ {^녥FF>׺"\ nߺYTOԀM#{R4Qj_e?@n=&6a,X8>Zun={6?}ϯc˯}ܫ{iש_.#mHE?>zPj$I(~n@\_ޫN>}a- ߒn_ž]n?u)n\۟ɿuגRnJM?<~޺MI6KX&5<]l5Ϡp I͏?ZG|[i {{O[ȳ0,G:\p6Wc=^vΫϽdG؞8~@~y{0/[7Z?@XC8WGb9oߨzN "R=C.G>׎:k,Hck) ,5}nͿ6 \刺^\.Y>$ԯ%R$jh:Ӕr,[͸k{]D!ON$` $rM?zj! ܋^_~$ u _pi7m{nj@@#nE׈ T.sc6o}}uܳEXZ}RE5#HN ~{.(֪z媓Lv;#ߺRx#AB\k {^[[IHh(i^A39m^^NB[H^PY{ňO{ǗZϟY'gqq~Gߺ^'{}zޡR0.y6#8>^_X[H~@&ֵ0QnS0$l̾YI+acǿvz,]Xo?@p?zPe>˨j!IS~GSߍ}zanXwđ&]PΑ}xӥ\.~ߓ}Su~{ߺ^׽u~{zDj[ u }}Ԋuu>}Vj#}ZzTÍ:G^UP/u;>=q1V/NFn"4'Wqx֍:0v45>"q5-ޚP^ʧI< yz?YrNM>pG߿ Nx9*GG}d_OϿ/ߵ gu+ӂ ӟ~:С&,l(~~ߩD(~] >?U~{ߺ^׽u~{ߺ^ _WAqi_?^C.yzqxQS>.z{æƸo㭷Y@#N=x|̶~?׎=ϭ9R?_oҧqɹt |#R?g׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~pr-~v{շ[/{Ϧn_l??@mV㞼.maTm#uN nOZI?c#Ǧk[G M?{V)׏{mG׺jR@?׼8t0:6&׿}+X9QxU#8Rů_Î0z} ؏ ؂8M_UUR>&<zy#ECq}=:$T~?H\?7oQ_>ޡ 8Ϋo{:uT_ր:G.QLokuz::Cm{~}Bm# }Gqׂyuw?UH OϽgϭi R\zo߱ízRKPR0Y n-aZuWۮ>aiմy7v<ӟ-Tfcc xo~zRG\86=dMA!!d[Hׯi=>CPWx߫zW%5̊M3iy_ح:maS0<XrG_z c2IxaPI]1T{{h=ZNh }/ /ׇZ5#-dzR~.LloWTڒ!~$$ܓp9]ErY -zu<:gmE!61}-( n}eVΟRx<ꞸD!#27 ~UXfQB%OC.s)G+ :6Z,GNxcL"깶I_?K~=u~{ߺ^׽u~{ߺ^L.PGqo~ 3I*TS:>ҩ${xbſKRu׆#޺P֒_$xKZ$ry6ލO^ <ʱH%q^QU#ǯz9NyZkÇUo^:%E~ono}Pk{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~qoS{'_*{^5o?=˭t89o]o]\ ?P{z]cy޴xtH~܃Acc}WˉpO#oGQ ۛWz.\X#?NC\8l-{׍c#pl߇^u\x?/~z׏Б{؋珯^4/Y}˪)Xq?? @ߋVx؁Ł,&}~ֺ 7Zو?)LyL,-=?uoutϟHLpA-D ?^-?:u8guWk(#?ۏz[?] HcOz'ӯ?FpG"~v9 c]P'܃{MFzM omfsžSW5쮠HBB?˭3J|z}A?^.C½hc_]a9*ن߷^$M-o_{={aidjV͸}ϿZ*/p>[>} iЛ}I6Ï~@Tt `ks}A:$?>o,?C~8{תzŇ-pu-mo@0:׬̳ -tIm ?l4aeQ ~?DPۺҖM,ōcbu>V®[詐YqԠ=e9xmv Z7Zdyѭ}>ͮOא}[z[#5S$25_a4xs5P5 Y~?j5U)йhtJy7`. ~}zAl\Hrn0[#zWG#!uRC3O>Zu}5ᎳU{ Oۓ9>x?Y}Tj`Ͽ::7P?Gca^ԟV[~@_ u] 8e¶~Vou⴩S0~׵[ߺ:`ѵgʒ~8{uُdv.$_~:ތ!wN9e'o~^zmI hޝd\su'ު:7j3?(=u:$,8F?өjA{Z5Dt m3/_ߺ>@zsZ-GWxS?q'/q(O>I=G8HI>nGgTyu>Hyzkǯw!?-}j~eO_OG҇"!J}TgJfz/s߅|Su~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~x^X>ߺXL #>=u Oro~[@"ۀ=$^\~OïaMߺzr>]cKŗ>}?>>V'!:OQN={C4mZk\I_'u0l=RrxS??O pǏQvP@ اTO(py/v=ybWB56N>>AlSN?rn 8޺=$oI K7ս@}P`|pWWЀ@?؃ǺV4>Wϯ0` \MnO ~#_q BF8O>ݯL3*%unM8u`V:|e fA<q{sW$P-b.._|+íW̟T4l~?؁Az#Δ[},nǐ.E\[}z;9qi1sí 5HtYFK}YK aqܮ}Kqp$7$}9߁z}0 }OnTc)PjPXI6?K{ to޸#?Aj=33ذ ^JqsqƝ1{skSv[<+y/u5}-) ,trTr,yk.xP-d?QtH'#/7#@QgGߺ׈Kx_߫^uŴn8??[|k] *xA:pe4돧@,^{d[ϿuV14OU͏GA׺Sm=%޽pzq?>rvE~8ׁ@SpU$szN6?ov<+׼N=u뿭-O{)ָu0IskTA^?=]{_s={m[_QӡqN`[0#aM=۾%.k_zMON)=Tܟ9 ?kYtOli WRy<{Эhzpb32= ݺlϙh7*Tc 惭^k_'u'"Yڏ״.Ůn_ݫN (v!O,?>_Z+N/<={E0:4J9zk[Lֽ:R;(mZO=*ijlIk_Qszp׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^LAf '{_qv/}{}O"?#uE^>:YA\Ǯ/$i/ǿuyS:8'7n o{[o~T=g?>׺Fj < uឺDm8kgnw䌀u{U#{\^y{[GPlO G^îQs}Gu^h[o?Qx16+m%yǎZ{^IU G[ߺ))hCVRU A~8Wև=c8B-h< j_Tr$#~{Z:5ou/qoh< $ak ^n<fP$O^Pl5ǯj':'CG{ZGXmdA߂~=5u:Jf)v}/~U&L5)`H#kA\xfe:o`~?Nm#4=[UxL4AWQoº?` TPڔ\QkpCNHlKr/mïgZNQbJȽITPt. [ +A^Y#BHRˬF===KlR#ޏ瞙f\}xwz!os6x^Oyިk[r?X v$nn-ߏvk]{ o:_Z:5?c{[}v>sPmoߛ?Z5Apb  o[÷*֑EF{IM,/``/ǤO{{^G@JЃ/GNs7q/_޳׿Hcǿq׼N?߸gu6Oǃ^x] {eHUk}lA&V:izs̩? r~_^zELB [޸:tzdNx)u=+{FtV!`>z cޒ$?=S:@p_u[=ӫ-: k O `?NǿyuPO:sZ?[Z~{[n .?W\M]B)ZYվ8[Wީ=z$>'3N4Yߏ~˭H[Zz̿Omכ >׽:8?*u>Q5?XZ:1/eOkݾ}6zR{Z׺u{{^׺u{{^׺uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~x^,׺T/_<>ޏ<_?}W8rkp8<=w䟯 ^]k:{7;}-_݇A :ԏ)LJ[PIqk[A?^]oӮ`z#>}=S˯9tHp>6~n5A.M 9s8KG׺q{ؑ&ַuy 5[o~@?K_vꧦeH7 ?x{V?xy:s?{^c>4>EJ-Zn׫f(noÆztU}_R8$7bqՀ-o@_ZH?ӛ*~][E?>o/Z^ǕlܟHQl>Oj̤ tQ ıOvꇨ^ǢPǀO:o`?Qsځ??Z(qQp Q6E?NK[[ZTIH=GŽz xċ7[ߺ OkJͧ-߱s$ ^ 5Xۑ`E_z#ӯ-v9TTc M\q9uj7R"f U#O!P bIǨ&:rU}Dy~=.C8<}} *6V?W#8>O^_:غF7Uz]oQca`ʤf\QߺϦgR'L)~?NcRs<)c~^dbv %2~ii$LuCӨʹ bԋ' ިI>maK'$[ؽ۟yںV%1JuUHk܁syu#q,B/鲖ym}l7Lgi7$T_j$~$l516ڌ@ P7y>zZpO,M7Lg=WWXiNX'ԭnG<}/hM aIFQBrO{Qc?NjycPo*P}E)f+ee8?b;QcRC$r~֋|Ӡ U#s҆ϧ#)ƝojETp꧍zD׽ܑMFC5 \/T$q= 4~T#U`=5^{ے|8xfi#_K{zޣu׿A׺{㞼Ezǿp:FJէ6ȷ \/t8lu.;X:Eൿ|kGYVDa!6Zx^\NǛ)?m^)?u.AlNpqe?'m~9:>}]{˩i{P>?ϽNC/u"P_X{U~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^2}t/7? lyu7$ܞ{GO.[ߺ==R0h]x uZPu#&ߣ/޼+?K Z>ת]^CWqԯ^? $y_׺~~Th颢yxql}=yg{[5׿Iy<[{^ußɿ={8unl̀_~}8П-|jԐ#ݼ1^e|5$,+ Yj#xI::@u}Y^ :dr^ ז:YW-\TMi:Ei҇o*g!X\'˪0OK٤dZHϨhzCn9i&1Լ?QU[MsaM1jkq6ʝl)+oBR0#yZIzZ+n5ZoO.1j/ !RE^eKj<ؑoQo>}PLY pkTioܟ~m wE)U>Rܑu#Wo5l8)?aTuw.<$Fax:3kCb>܏{4֩C)hD54܀ ?RyzSUԫ JÑ~?{Z#P@dE =M&?oAߪzzp'?t] Ut*7>1í,)csbE?Q{=X58u.+jHez:55m]0EGOo׽zKϵS e,܏?wյco'Z۟>A֋^F$Q/nO>ϯjU!j\}?zK M^yQNSz-DTM[S@ㄣ,u\_?ת`rT@bT,ǎ9P\㟧XcZ葇굀$<_,}G X?-#^Ӯkx^=p8}qxAےE7~4^ xuDA.?s~|׼pҟS_髅؃ǦUOkߏZ9eaO{ԕ6}3AbכߗT֪ MuH <E:/ J[\H{?}ϭ׭~P ゙[2::#?{z)7H$r onza n8[?ޭޏ[޼pe*?Plm{Y(H?=`SI}6S9x\8?B*IC?Gߺo@,tſ!kmQz@_UVɹoWI_}9ПuZ_ڢ%uj}ڢ,ܫX {BXmM½mq+K>x s>_A?W\ 6+齿oq?~޹YNfoޏ>z.|?԰OP)P-f?N?{՞0I"ʃݺǮ'\s.\u?rZ@8u;@-￯W9 T>ӟ}JT׮ i?#WӬL;2,ar~.~L={=jriռmP ŖO?A'YMNBgTqE?ǽ\u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~EP/~s{x ^]‡naQBְO'{:z gxDdؕ>B:|f#a=?[~u^l~= {zY}x'ߺz&#j։{q O?˭ԎzrP6caozS&fR5׺Jdţm\V:n}WPy}HϟMOHߺ޳ZzZ܆3so{^/$)lɽǽTulLr?ֵp@v޸үLpIU y6{U=,1ŀߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uĢ )^Ggt_uxT#IEnߺX?=gڀ5N=_N~M,}שh-a{]Gemz6ExG4s$:('_"{:zԖO{:zq_.>bnA6]  az A[Zᾁ߸s)i$(*{OrT$ځ.@??ՁU3Wll$rnoqcW_zh[ڡ#}~ptemq\NW?ᄒO>Sc^]) ѮiUcS^"g[\l ~m+p^@=h.Bu -o=҄z}p(E&+Zژ:!G?>9Z? 164@OߨGZ:֓eXeVrO.q׎%́؁vV`k`YHqpogt4\&^N%mk?wuP>L_}?{ru Im6C@:1Ы ҭ ?s}ptܠ @&,E<}{n="n>jjurkyGM t$7#?>qӧn@\׳ל N.>z\z{UDe\e9טt,==y}t?tz \wog˧K@|?un?)#w{?8@,x>cMZ[~=>uS2b 9n?>DЃtYA5py7វxE} gqw[ZW׾μ,,8?,>_߼ˮ/jܷ6~5=o=,jbG~{O} P C}S׺u{{^׺u{{^׺u{{^׺u٫}I/~<^^#N:@UsoTrzWJ̢ ==k>O%pkKmߨG[ '2B ??~Ju2=ZyoyoE2IR6שu5Zkttf'*l7E J1,aXU'_ U$ 6cB3Pp #PcrŸy˩o {pSo{ik> [xuu!7="&X{GZz2.9t' \{{˯S=  {:YRՠ+_~^Szmo{Z~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽tkgٌ}9 _4=k{7\>E:>}usbFZC$2Įop?6*(:O&q{PAs͹{zt@O-{##߫׀ºKMͮIK@ׯS_N0nqB}ֺQR0U [~9sZx9DSߪz(s!^ kۋ~Mu>)<o?ǬդDj 0XryzE[ߺ:𯋋\'}=4]@'ORրn"1[@PK]ʱ0PH<M<^B@%Ao/N]-b.>:tD_a([Յ|] *E EqfAֺֿj|E;1`@$[݀#޴=oU0zpܯ?P">X҃FO@q~׽_am mA &p IyVފҡM@nA  ~u^9=A5\ _zMz>]'k9ʼnooȵ>\c_ϭ\:vrOqNozu_>+j9?[^Ϯ76 Q~>/[.)L_\iryj'}CfTL$PUtI*lDyӫ)Sצ.itG_ukQǙb"?ߟȸﳭR}a|$\_7 oZSqp Fv scm'GH^UR_N,@6mC=ۦt:yRȉdUk1ߟ51NZ='1d[,J?~>ov G?[{$uӬIbaY~'C^C^ [Ho[aW2?IضǽRWz }=ڵm_@uy?~?>׏]8Rtofri)&pHg~ޞ/pl}ۦ+{Z׺u{{^׺u{{^׺u{{^׺fɮKq1Y#}A>1ӣuH* p8O]׫}mczq׽_[R?_Ł7׏Bب%m-K qTS6X*"?':w=tk{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽tR^[S?QۀӪևa:p8H6~㎬(zG׎.~l(zk[~굩=p?t~={Ϧ`/b~ܟ{:z-~M~cyu&6x?̎o[8.*$ /{c=BZ?}}=ox{:?C {˯?>:麭C;X7oŽ]D1'16`O6cc[ߺ]멈&߁oX*X[V<qk:)ǧm蟦!H]?_>ּj̖Yn?ul4M^ՖYܛ+#_?Vʿ:U]*y`op~}JTN-ME2H?[p{Iz zQ%if7'P?@@q#Y$pF+"M7ï NwUky 68[oGTqNʠӧ@;O+btoT9;m:ycnn-/\ԛ:0?A9Gx1Rҭ qcaBzNتޒpX]l96/#z֡ztHyCǩ~׺u{{^׺u{{^׺u{{^׺u(O6{z@>uBq`W-R<:m?~O^_>:Ͻ^KQ͍`/ѰIzďuQſ3/I* {o?ϧ*x\m]OxSU뿨q){iR>?Wָғ xp偹'}p7]T-k_'i86\Xiom'Oϯ^$sx{rH\GWW]/׹f q{ .ƥ,੽ַM߅ZzҞ./xO?%h;ߖnOhjx_JuC06CTѤZ8n4)0B>[0q~Xp ql-b{{׾ބMrSPG_>E:U{U׺u{{^׺u{{^׺uxDڪEynT\?{WPZ,z i?T=rdCpm67 O{6ϫtyF uRԓ !] M"A?R:B(? +H_^?؏߿b?˯Pׇ:.9* _}G^G=M!A.}6!MC?㎫Cna?ֺ{{^׺u{{^׺u{{^׺u{{^-P%Gߺ-Sxŏ8z~?~Rz٧\J̿T}ATz\Z5m8@qkîËs7֡Dd"c>x׫tۮCrYn]"ɚGbǖ7+=ztc=0fvp|(z]0_s{ǺmmGs֍zΟ~Syu7q{}ϯKO=zu6?[s1֏SDfNyh%0as`8_D_ku~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u/}M{H@FGNLר]qco{ԸcFoͭ7׳צ'ۛ/tSP5[Sνn`xeSkXq:^ZpC[x^y<N`E$\[n8êI qk}.?}yn-_>*PHQ<8\:?&~}Eoêyژ\R_ycA o~Nk*7~{ߗ^>U2%aoP-kǧ\YVW sn5^ddqNk.̼Rk&@ة}"iy%o1'P-smg'_V8w$n8~o<2p_?J 3p?G.E*t?ǟwN [Z5$ C8af~mAAӤ]#u*?Žmm~CN?[ls{'ӏ[&O/xu_2E="Ĩ,{^M8_LdFi`EX{vu$`=<@$OϿyu`GKmIS}/'K~]{ߺ^׽u~{ߺ^׽u~{ߺ^׽tjV 6^`OՒ="ԨQotY^%|->:"6$}sk\ZQx 5W׫N= n_ۉ~ um W8nJ 9 ~^uWU\ӠmU]Uç|޸9=x2Rc`l{X^JZ~^ML_emO:,oz55.S͐ p=了u׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uP\{^鞢RXK%Sp9cu<)f?sr,T$ގsՃϨ϶,_L7>}j8t&Ԡ`Z6߾u$3X1_M>Jp^zmf1) ~{V}QG".\5?K߀ڵ&5J ` I\>\+]:yj uX^_vKAX׏~5]fZ4nyZ_~Y3(7?_߇ZGYQ_~No"o,=ꠞЏ}AePCuG}W^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~t@abЀGϿu]hO#{iм}=#F(@uT(~qxAfߺQ M7>NSBo}zt06?AX{ZPSua?<^ cVRQX_~^&c~'rqu=Xq%H"/pGǏz|x@WURXRZ&=9VFDԺ//mtUI M6e'?Ѿ_&_Nj?]uorcߏ7?j:i$ A.*pECW'_]O"J?`< o~HKXi\@7'X MpOǽ#iHjLrC{9/W-MmV?U;?1[GSx8=Ec@p~:O߽ϭ_C@kjM7juߺ^׽u~{ߺ^׽u~{ߺ^׽u~#wRr2 }7Z>$_::eǽ&Ƿ>޶}:_n6:8~~#ӯWӡ/kq%zz җ0Gt_G1׷x{W?ǠRu 4C}9N ӬWy>\~O^<! t#׏ t7ٚ"[Rc{z׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u[?ۛ{^;UՔl. oz'^ϧlo:ڡG%(orx>\ִGYZnM6Rx- J{ i#i)UK]t۝O^ |rݔS2=hJJJT~=N( 6[sJėBmPn?}ҟ\|TOfCB<V^ϗ] & Y9o{׉]6*O{>13a(GE./p?ֵWnM'|c~e?qD.~[ߩ׫(f[q"~/ǯTt[~]d׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u:_{ `G$^1/v<x1~-ɳ7Ԁl/k}34ӫ^5<_a{ۜxS qG䓩'>t%CQ<ߋٚ>M##pwEC2ݍ}8WӯcϨq0} =={JuM~%߆zu?rO<]oNT {r?>]ov2Mp/~Mxgc˩1V)PM^֨|f6ߺPFR_'#Gr>.CجߺO}u~{ߺ^׽u~{ߺ^׽u~{ߺ^_rҺk-R-1CVpISzvh&Q^nů6wZ>Û a#z|]r8o7 {-;'U,?f=hgkaO7O~zm3ңߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ ?C_I?tdhe^-'{ƿ}N`c~=Tz sr6 gJܯ?A{:'7J}&ꓞT/"y![ Vo4״S \Q^W;C2Ā8{״⛶3Ɍ \^>|tO q{k{^+NNK'V$ }uȰ׺ȿ{xw{^~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺXdW}Gߟ~sY?~2L\Fڿ}Sƒ[؀k~?7ǫׯ6[?qu};PF@[,~ǧZ=p?ï okX /Շˏ]7{Z.s6\N?8S?uʴ~7>I[pM+b.4=\d6M:]k]8b:1WSO$[tPys\xiä^+0F7㟷_tM# nx|yX4 ƦSs'k޿U)y$%Ipn76@_P$¤ۛ :kX̳W#^z5=7ʵYe>p\ zUzek@x:~M=RD'UZXKb ?t'=S.'Kӭ~xRysK]<^\[oCAin4(BZ=֊u&=Ϩb{(DSڮ~C׻zsk UoIJ**56'VJtC=jl}\z=l8 e).k`:Qq[Jִr~z$u {jێ~߅O˭Ǭߤ{%dP~C8SH" ^OMa(ܒ &\?o~u59tSY}h<?GSTuh&T=ꞝ{WR(q*(Ԍܐ@򢙤ziF㟭ty]7em"~9ހ>G5|VKU`MX8A`[ߏa_QeU kc[l> T~dPmP߅Vs~ϟz5#˨*cpUrU-sr _~}P:rB :ٯ Cc>im,~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺH¬i 6 z9Yi^9Pn.c}^{W>ϫu׽uϽC~~.!df$r#Ou4= nOm p|5ǡo~7U{"'nISXN/?ucԄȹxu!>GSߺטLNl>~}{*4[?s_oZj ~-x>N-؋`zi }P_ߺmko?m{koztRߛ{T@o)kC^p~^럿u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺXD)kTM׺L ?'~dT5A6炙:Co{t=Y|?s玶>]9[Cnn'{xδ"ַ֫I-xp.,q An?{[^cc҂8ժ\P=۪ڇx $szxu?rlE?N=]1b~?_>ֈ=g6dRO>_ޅ:8nUzMŅn׾ߏ{GM6"r[ FzN8PG76_?6ߛWzK'4X5}b) $϶ۉ;LTp〪Qc\O{?~ u.M~KTxgSVGWie7[^nToֳ[j=/([T Pێ?{w|N渘}I "/}yq_.1Hlmp%~\9<~"_nm5gUœ`/?Sq{3ָ㧌a8RIۍz]Po?UW\2>6"z,O#}pǨZ }@nG֫e<-cbt8׵non?[g3\tڌZ+ٿ^8uFyYHZ^?\ };Bi%Xac{zj4zg矯OZ# møs5[nyL@<O'z`vJv*HW#H{z7U.^kin9Qž^Ϩ޺erۛ~A#݈ZYj@}GWz<:\uoIu] ^4{N}>tnQ^Hu}qҬT!OǤB%I"ÏkkhA.ԬmiYFw5-eHy9z'c%&\,׃#ݣ#wyu1Ov 뭯b޿{B:s ~ܛߎ.O'xu0 y?~cE ~QǯS}?Co{[e&ߺ\uX$?Sߺ^ Wey#ߺ\u~{ߺ^׽u~{ߺ^k7A'T$- k%W?P<߫XԞ?PzϮ)~_uǯt'={^H-uV:vK.ߩEXEr3[ +sL e$vћ{:'@ {Ojc+?hְ k.Xp?^Gy6oG7otDʎW8ݺLA.nx8tzo?~m߀/ Z)Jѽ ncYoɒֽo=VQү +r. {Ϫ?:>MqaŇ׷tzd~ήOۂkc?ߎE[^ͬ/׸gYA$^I{ѥGUcACлD t`GK+ csBXvް~T;>&~XאES Ip?TӮC9yq둬@I uRzإ(:T"$ ڭ6Zp?ZKcl?_Gt]VW69*>4:Cfgu?Qk=yζFiӂyCpx J~ִ|[z-eeq]{H=Kyb#UQb# g=(#V1lno~]hJmJ?>ֺ{{^׺u{{^׺WO却RyԤ}>]lSϤmVYʱFł*;5~ <MlF4&7m{WMsTkMň @_jF:ޡ5rn5:ԏPE:ޮUD̥Q7ިkE:ڕ"!r9O~ `tۢ/X->Q^( xuƽJ~yϿcyu61|klcߺS%eMC#z.-uR:2P'^??uZ~HOuìk׺u/{[=rߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽unu{:WO~x0b@ůpG\}}uߺ\<{jsq{̷$ ^}mo~^ko^CxIo->xud$I6ߺXU96-P#865[ߺ^0`lO~"?`?ߺ\uԟ>Eu?)*:Gz飌2qԯ^ e@u\io{^7CG=TŨP$-?~uP)n?PEZu12Mcש-@_O{Zl8BXsbE'TzQ ':$ieo@xC=a"]B!?mAe5=blMO?d#?n>ztE$00+-o{/LUIbnM؟@}ku5'~b*Tֹk@[hx"/Qd,ķ6snCǪf=#?9ںh}vǿ}hN9ZÃ<?=p=+6vmb?HToKL-c q]Wνs`T^@PHL3s4KA~}Rk?oۛxzy 1'GOSн@tݾ~X~==7iվ2/{ rG_OWNga X|B~s>ONÏ^ߒ8{ u9<\{NzK]("_KpGqoϫZ~ ~V8\~G[^ܭ>ĐlO{hxp`on>6x'ߺ^׽u~{ߺ^׽u~{ߺ^׽u\>׺b#bE=uL} a-{N~ߺXN#U?ǿCQǩBѮk}Is.rTG׋nxO~$0O{9_PSVz&PZg[{\?{[￧uì={^׺οO~g]Zߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^59'B^'uz؛t'3Zs]luH[P}o$P/.zPO`>޺ <\}Տ]]m[M-p־C,̜~\x _~}h=8X$-}k;͈7ouzO^[vn?6:3NySMvs O%F߳׺w$Dl@$?{օ8^ eq[w- >c 2>Nr3|mp>HM߫֨Yc `#9 ǿWPqWzv$6_Y=u~{ߺ^׽uDl?^K1\ +NzSpA>׺Ko~:+ⴔcCk[H8:ak[.:VC+~}n 'G?+Q}:OX ,G@?ҝA}O#ި:ުmzaX?Z}=  usn>^&}?M6KH6#{[޺H iK%7$)u}TMEĞ@XpܛϿiF:ޡF֭)7}6'~z cX?݇W?]\SíT<#::+ `Ac~zdPt /}S+ t8s:N f^O7N9L=gz ,-A_}\,?Sp/<)Nc[5HfRtܩ$ }|3ŌmtęQ_Oߩ\=zDCK$޼HN?@?_w8c -iJ'}#߾]{_~mok{(鐀@Dx׺/ϭ8 kF:c ZZ@K{M5"RZ[5}O?^/u~?OZ׸6c<\}X^mUP`j OZa F^~=8{^׺u{{^׺u{{^׺u{{^׺u{5sI ׸6n*NUSHVAoiB=V}?ި}zD[-91֩ӜYG3m6NF_^HC=xWFs}B_{޺O\4?~=gϬgNI ~]yhPrq~}zMT ,u5ck{^_Ϳo ԏuzOo~]dv?a~׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^@X}u߿u0|[t0@#Z@?FluqPmͿzLKߎӀ֛:t7{U='O_qkǫ^nZ׾^soߺ=K<.MoǟZ?.?޽]'&Gձ\[r/o~?ﯽu{G~=ZWA>A׫)̨[H?O{TO{{^׺u`k$A՗I%Q{Ics(8qO/Ik j p~W}dj_uG׋W={IFzO32k.I.OЎxj k\IlJUFo}NWPa{_ַ*8unITcno, ?ӟ˯i=cMŀzrʕGڇ^zvQVKcb q{{kíizQK~D٪H߀O[ݿ=zglj!|ֵTuc+}S$\j:upӁ升 {֩SDP(0:o^wuRřX:߾]n7˷Byg*AVC2lدӁ#P vP}I}Ҧ}wK؟5'ag:^G'޸ u,[{^=  6y:t>aE۟W$s:mk{^׺u{{^׺u{{^׺u{{^׺uV !MͿr>~`:rcr>@aG?P:sө Ŭujl?^/q>ֈO!]Mq{gR;C@,.9ݺNqg'r[ZNv)҆9 Gu^a~c2k^"ߺ\~wao~w{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^CU47~׺ GO{^RD]Fx+ՀM~=lpe#ZNֽz'oJ }[#܎[޺kF'׽^M }4<~,LqSӻ׻=k75_^G>C~E=dՇ~XŬK{yt"B߼ɧL}>q{pt6㋒}lu}GI>ꇏIy<׺֯k66 e㎼F VxvMq)_%W >: L46 }W{[%^?:uSkMѭ$Sָ1t-ͮ]y&| rLbGy~:{{^׺u{{^麦i`/c`I ׺o\_?_bz *de,TҸEU>]oM8$ko!#7{jSZϗ^4"y ./nlzqk4 KO^Ӕq$iGAXߺL VIި^V_q{:r~ֺ6}yu>?[ǽu)HYV CK*WR>çS?/kkmϿu>7}}u?${^?^'u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ\t'Jnx'ߺx i8Y@[urXcCu@ _׎z}uo`6 ^"n ۏ~RxѡK1_a$ֺ (j-k@@Su/ȧMM@M7bֵ{=zp뉡kA/`'P[M6?_/Cǯj눉-P~UO>+ZGK{W Ai_\}WzTaɹ . ߺ:?垵2؝Fm{[ﳭg"|v ͏aH3ߺ^׽u~{ߺPkTD~4I~@OQ^Ndsk<}-|hzw'?՟|V:y_M&?>ǯ]! ~TR cck[߇ u>?]qtbnackqƽ4/=5@kq~=WHIX3b9ۋ_OWJSAtGN}*J(^3ߺ:?njߓ Ǻӆ/no?rs׉ǧB=obq.A@*ėT}M@~bq^)t?W^9\Z^?9{ao[M!aTf7#"hgv{Mgϩ^׺u{{^׺u9&)B YHFӬ͇|S 0ǫSR9?O.&K%* O[qGz_Ϭr٨_AԎרRMgHyAPH:Ne[DoI63L]:?~>>)ΦhTnG,]CumBlѕ[f6?W7l{Ύg͇ac$<9_QDC86AA=:;Y|券 k}ho?>)euB#O-k^Qǩ.$=uOa[}~>׺̞2x׳=hM?P/~{ߺϮAXߨG~׺u{{^׺u{{^׺/׺DJ}E55F&ڔxNql`:H98޼ÇY~]KO|NGSߺS޽y:3fb8:UIu_U */_\{^ qlu~==H׽uMǿu~ֺߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^\_7~׺Z̫o~tS,䪻'^HT׭S >yӏ=8t W[>omqkë'?MͿ}uߓouu _^먎s Oτ44;$aakZ@U_'ߏ :4@EٮotisGaӓO>FOO߿.8u$W'׸t9io#׵OgTGN]ٽ@8M[EG~}Ӫ@K9dYKAx [ޱӊ}g,OޝT}t8W͸#3bx~ޟ?nB ?OD筏u?o^:moȷWҧG_Ź>_>)0!?S>֏t1mcG {ujP}@u2: o޺ˮ^it2>Qo?.A+Ӆ%lߋ~:^WVU-̀S`/k\u*znPR=(qv׏}Rs\DSKݿ#GP֟DSC5 P&N xuu.rͤ6߲8zu<[-7[~]Eju6-]Ij߸=w~ ?={bjcosň[ܛ[ޱ׳QP_i׸%$Z@6Ss$u\g* O׺ؚV`^@xGˬ N~Ɍ]6xPmSX(-QoPuFp;jAaNzPm@p[ߩ֪iTдf x }k<%5tPu=筩8fuR1X[Giڽz&?B-Q_z[Q[ X"j}@OLCc;~VR/ֽAzЗ?oů?@9&7ӿu{{^׺u{Y@ibC<{@?o^ ]0Uu" zxߟ<^}@M=ׇ^~?˯qykZۓ%MG*5)7c}=3pE姙IWO~ց]5%I@`={+޽Z1,u(H{ZS׈#Y]ŝC?k\ǿu%??8[Q[Lä5"ֽz>cHo7${^Ql%seKğ{u?|#q_._^8vPH$~=yq/UNTI~z)iX?{]Hqֺ u{Ax~]dO~~]dߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^GO ~׺ߺ]h~?ǿug=/ydOЛua;؎^\$YmŬ}`Ak#jX%^=:IA]Io{4h}zdYX{ۑ=iŒ+qԴE 6b|[ Q։#T_y]6?OLS5X*ojtXoNEդrXb,.,>}n8=vupK $ׯǯHߒEÃ~ImtZ})pH񡕵}x#MpmR+RI9d 5Tn$omrib<שּBUY_P9'odu!3<ߤs}z>ִ˺![[WUO~c?ZO/ˬ˺٬lLr"@״#=)VTʫ!ݺ{^׺u2#[R#[AuìmM  x?McP ɵ?Ͽu{^׺u{{^׺P/`pH<}>_~={ˠ3%F_UA@kӟ>=&]HPy&Am-u}{kEU` LX?߫zX #$ۑs${)G}-CH~^5 J6_>8˪8 8?<݀cɧOycϬS`y&Ë}wpAy<}yo? ut/?_^ijv@yB\O6{OZ#ׁmn~k^_Mq=6E(ǟqSӋ:Ako=~άGYo{+և{)׽O[7+7{ϽyuS}2DmG7r ?}zz'콅 nV>pz LfAF,@o^}]OA yroI{IwHk֪k^%$~M7SA'\bޞ-nOGY馑D N^o~Zб- -َC~ȿ}ʽ6FsҶvn qo,e<nnm$O{u0Uӿ'I'Xǿ}jwͿ}ا\މSXT_~ad~c}uzO>׺G~dߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^D\>׺ߺ]Oue.]]Xo}oU:O~O~-u N,V9}j>-R$n-n?ozn=]hFsn=ua"=Tu_Z^{(}戴jj$asW!/ozc˞OЋR,8|<֩!V؏\{֏u 'lЀg:[Wg^POq߿uu#O-Ooީת|C%xi?8{/[jLyU?yEopM7'Z?:MkVb\[J}p j&`ܕD*H>^9J~ֺu{{^׺u{{^׺u{{^Ā &rI>~4QD|~NB~j ߽q~]aA'P6eX_Pmo~9\~ΙϪ<]ly#Ÿ˯}:؊c0Z?S{z6١ܥ1?@ \ ްs׋|I2Dio{Mo[߅׉J) }@{-j%gF$.~u"\’p4ugӦ6lW]MSi<6s>}X5.ɇ壔X1U.dd5ZYK1)cbn q:O[rzV. n,{^H& ib!ٓ O t?[p#*PԤ_Ou<:qkǠ߁NuwU>rWV?Gǧ\o2Iqoɷߏ{5T_3R=\% }l_ޅj+XTS ʅ7,йWz=8t8[PKX{ӽI[N:]r?>ծ:?z:^tθHX굹~t"Y`nvuFt$+kӀæAnzpV RM߱NAlx7"܎<pԕyөQc?_~^'AW#O~Z*o<[~j(?P>OAӴY9ocN @c}t䭨_ߺ\u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^NCU<ߕ={&(QH?PUH?OEplAO~9cgyS{$HP[E{^8Q*>O^IPH9VV{uXZo׿S9Rf7X{ߗZ4lL0s͏z:o~6uIQ^C㮸< o׿|Sh{^ s/•YEh(Oqo~"xq違 }޸z:Op : ]+u~{ߺ^׽u~{ߺ^׽u~{ߺ^ o~$zy 6mcT=_12E"}fGMxulҟoYFo F"r blm#zuM2I!5ٵPcGS!_.\ƽǭ2((1{:ҫh<B9|6(jzZ p?Iߟ{4:lA dd@bX~[ ufqsb~_ڭ ~TPu%2DZ?ok\zҜzE/iKUcma!矧xj_uᠸd1n/#?)PWxǧzZOU:tbon?uN2sUr#z:^5;4aF7n8@}>=4ͳ%]:$e DjA~5'P5hF(ң,>}=Z˦ɶz\ir.E^ ]K␑="E66߫C[OH}2,~@=ht5anUzE_שS҂&ﭽuߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^R?޽u~{ߺ\\R@soǿu$UPbm‚=Q^p/bOzyuSP9ny 866gV:{BYԨ&=XҟOING{[qY8oz^'?Q߫֩!^@&'>>}omJԠ\_ߺXgZsu?'cQ?:n ߺ=zWGo$?P=Pu&9RQ??OuwTckOӓ<'jm_~iש^OހMmG߿uZ?Q]G}Jy?Co~.2+0|8>׾}G4q1$i?[>}g-7USS"NS^ ?{)֫x-QU{^颷g}BrEo q{`g~ 3*t$^I"s_^6"TiPk}n-=ǫ :.O }@~s~?P:aԬlNNrXEּzz_>_zhdt$I_t:{1's_϶<^"oȶA*9A ~]k:fm$ؑ#poX}VW:n~'~8RCST ,}uƒʽ/}~{ߺ^׽u~{ߺ^׽u~{ߺ^׽t1vqk} [GPTut= /~,.^=X [kׯ`Տ?m?}x.\:v&OdQ5u[{G#Jy-E쾯~?>;)А P7b?Ozl_gA,\5"׏EӀfIjY `㩑dQr* mFӭ^=a;2G~ &^vz)7p4VXܖ<[R x|VVҤXk($>OmӦB#HI X? pnBC I, ~Ղu13٬ZT\jU*|SL&G&i׿pjW^0 ioϨ1֨kèߗUL*}Cޛ{ꃞu@Eoi?8y֪xEE0RJO.y7oS^1xApGA?߸gN+0@ckߎDc\9oP 5WzlnB쩪ĩa`?sވ'=ZG5nGP x\E=J^K?Cp^u6Aak~!-D-_~ií\l0}Bi}>4GYV nT^Hu,KZ.@x'#ޏNBCq~g(x?ROkD~O?Oz]O{]8?u>.@ӵm]hS}u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~c7{^nfW"9{ן[:s7X͛޸lyu' [|:OЛĆ\ݏUw_Mz黀,,.8NG|kS׍{ؑouNxBmt ksxuX?}ÏMnMr _[Lt@lEſ}:ѭk:R@ׅjzjG}>ZQ|$}>xu3bmzCpn>֏Y>T\3^?@?7?DžNZֹ?[~=e` 'Y?8x  u4iYf!j$ԏ{z^}~n8_nWPLU\p@^맞8+OUzp?^^C1Q $ ?ֳJߖ#ƥcשL꺈W>׾}d"~Dz(䨹l~_ީׁ lUUao[G W:L|Li?:q>H"!Kn,{{V2?IzbQ} gTֺu{{^׺u{{^׺u{{^׺Hn@^P=^ac~ӫNWM }xŀ'~>?O{^?CnIOcV?ퟗNu΍U'z}E'>Zxt7a:^$Ho?OnyoL;!\X\?O޸uph*Cv^qom8s2Wߏ^^yoo~3׺cc1^ 2VGyWt0Ze'HC3DE{Эig+C-e&1$ϽGL Y ޥ[𡾇Mku?ӫ_>0}= Hk'#i O{zSH2TA=V]Ntej {36ı~- .iKyR'Y-ߟuzB5!mzoi_'{M?ҢY4(+bM'),XCΩk$[ߎ[x-qeA{su $_zz\Oԋs<}=cx#aêW4z \~=cQ]~l~?|z:\pޥ_/{Ǯ<A=,ɹk7Ry[+ 6 $?~:6AMݾSom<lS`] G<G^\fw1a7O7:M4nH /{p9?W:MYQ<0%@-{_߁%ֈzZI 鱿MV&k2Ȭk\j$\7q>Xޮ0:¹<~ ;PnT?={HQf\RckyB~.oܟZhzΙncك bEOAhz֚T9erA.Cixus>ֺ+:^=rRO?G[(#p~u\Q#{G2*:ߺ|u{{^׺u{{^3 zy՜ZB%s׾ П.jӳ]m6q>Ԯzb, ,E@OFiS"Q7Ղ׏Xu#W6O+~AyWdX ku3mʂ kޕ~?~c9d4 l'ޱׁ<:J078kZ=׏$'~ï}ǿSt5O#}R={=bБٴ( Vj$\~8zϪ yt.L51Ea6ϧT4ܴ pt?y~u1ӠPפ~ON^ȭGX`Xzl}gГ .oʑI?OӪ0B UߎG??~㞨152ݐtvpI [H[ߩ:3xaIn5}l=ձdbC~ 1$ k-}К5:&ʧ5؞O{5=lz:xt6X'ݵ>Wק]X22\Ρk*G6R=ϭhSGCFVAquZ#uu ^KM _S<SN".>@<[:*vI?PT}WM?؎A6?ׁ3mPB?0ߺOQR>^_..9M{YE,'~.i5 an~֏Ok׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~u>ߺ]hO+$~wGW~׺u$ww`f` \?TahA}usVyQӬ +q{]bFxPOߎE:TbuCo)ǫּ)ET {^ϗ]2p>Cs[ލ:*-6 Rbz[ ~<:8~,nH>o7?=u X 'x{=MA Z V_ֽ[XzeXz8X9{x9=wo׾}uk~E׀[m{5Z u>ԊxT"_>F4r-{o}8a?.apO{,xo#ߩ׫Q0?<2zx˯j ckr=uSǯWe`ԡ??O~=lt7astn ?z=oˬRs~QϿPu=5@P,i-5?ҵ5Nu-K$gZ#I""܋?qꚎh~}/i\<.y.FntפXz/?|jHj׷bz qB32 4HuR@_QA:ISȲSߏ^=Y1Zı>EVT ?F+X8$ݸkV#KGՅ{QXt**!&Q~n X=Wkֲ:Nfԅ<ǽziqK.`{רzM*LѤ$ 'ҢqADW^ԲW&yO~:fP <:6ź)nUp}dȵcz8Ź(݀3y{/auO.rDK _=Pu-rPp' z>S spG&Qzt=wQsRB@T=xtr@"Z+Oj'Bi7L?*>ON 1uc=K{8^/q=FЧb4EmO>|cLN&[> ۂ~uY#j! n f&MS=>(*H"jּ45PUnmkӵ7\[~-x>kN\WC.}˭2JG[q= 1׺u5^ej>\'-~cY<)B-U<\g wG!_$IS׽Of݁=_^_ݫֺ?zzKbxaDWh`UP4Oן~#hNV=&j|}I ILm6 8$MSvOy_so~uoRo58u\=_~vh!_Sި@z5]鵉~^JݫU"4 J?ՉM"@{Tq)Pn[H$?{f2n8Z׺)~gTu^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~p%~OuWV{}x#u{^׺uVQbA ׾]xT@~%{^8Miۏ~UAG[~{ߺ] A8?z]hO+$~}v?׾}t4b?Gu5, Mq£ ERx{X}z`o?u5=3BnXmmk{h&dPZ{^ԓ y][DOzCkZ䵬?Я^xŹ $>_ǿW.UmK鵜^n@cžǏ^o޺\Cbl-so>׏N YlA{zgu7#8nM궎KA! ^j:dkY\?Q@jM+/6%n,.xmG=l^Pӧ҄o셿ǷzdLy kXHA =?5?aRةkYuO _Ι?>Y*䬁?{uaB+FpI'XOn=tWWk#-}uSJuխv$`IQûUSYn.X)}OoZRTk*OAטB.uMu3y^nZߑ{OW]9/Z>\߿uzP[=WKj,7+fNN?w 7?~ÇJ\%dͬޛ:As4Uf#UōNTu PtCή:rwȊ8<3MoQ}zO)$6.av.8{G[FzsqPO#,M?߀'z֟˩ru}YBk[}Zӱ͊?O~]@L?ÇM`DgH<7ީM IuQ su~JDg4co<@7?zޣ^jv2O}AM/ïj_ٟ8xٜ2@loaaX^Z۴n!UI܃oS]%=TSK:u`=+W9?W@UDΏ k؏*xu`@/@/N ucpI$}x<GT Wu,(]r` ǿ 𓅫[)??މZz#AnU ͹zpcZj T b?P GJ{]{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~qQb[7Ͽu{^6V?Ix~yoo{׺ߺIoi^7-_zq?~?}}tYm?ީD:HoPu!x \ׇW/ -kG>Sը똬EE}>_^@2:҃fP-n~6WS 3sko֏Yq3l?ߺ|+ןcccz=zѐ},mc#ߺ] &ޫשhcsIao{H$I6\}I/ >Q׺׺3Tu#i cu!OZ\u޺X^n o{^bǛz[׮?`,>UnnHSzzc \\sx*x}>׫N'ד]ur qYخz-=K$(x)ǦAT0xγM7>Z#% īܲ?r 0P/?ȿ,/M,zR&n-qƑŏߍ|(Pʁ`Rt$r??' W]܆:<ߋ[޸ u쓑טqcžV͇  qӯe -fSmdq֫=.zu&ʼA^>5צ|~ r0ćM͹x!Tb}%M0ui\Jy@5 ~O}ud~MAӣZ6[G|z^HUGO>:s Oíyӧ垩N^ɷ~,>#lr/o{վ޺* O~iֈM-#ǺYe7>=o]+6TY#p &?zU.&bIb<:oL׽u~{ߺ^Uaf_L׮@X>ߺX&u ,+8G"˫+ǨmX$VdX Y 6^v]1:n)ȳ%{:'馣d1Bو76K~ឬצZ_ψSյgnem+`} } ~|4eJʟ}xu5Nd@oN}ut(9އjzaEٮE}Jޥߟ~\)?}ÅzȦQscy:޼$zX\-qnJxܟI6 Ϳ{zh׀h~׺u{{^׺u{{^׺u{{^׺uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ o~yJG׿u{^Sy?#O_ީ?{zxC~n-?^^8?K~_)xަL/><׺`ۛ{ߟ{Vzq{?I_W݃ Ÿ >ZА'a{^3XG=9JMEߺ?.yXz8_}߸ǯ\ۋ^㛛}GﳯXX?} O{T|{7[^=10!5I^'[}Ï^u$Y^O?SއV8uْ@E$$ڿ?ߏ~^s;_-ب?>Luy#sclG6O{ce ǿW˭uV W8߇Z95s"oo>@1hE7.$$9>ֺp܋>_kש/!6'}8T$t_-^!n,,8׫׮j>^㞸I Ja{'WtYYP{k?zuG׬' O}K}_Q_X~=ꃯxyu1srXZ%}xwIoK܂ ~Mſgk?WWOP<blscZ95N+'}-o?Q6߈z +/ !.[r8o>MS̬TDMn@Ԣ׷N>P9qo}j^N1<0q9ϿW{'˥]H+`^:o7[mE:/N9% Oůjuà (?O!| :OÑƽݬ@<~?~׫w'l>kױWյ*omdI7|vk\u\s^|I'Õ '1F$"k _t4=Y}:Caſy?=I#5 >u_ן][^$I<Ч[ 1L1}uVQٍ}Ph>k ~eK{A9Z׺u{{^׺u{{^׺u{M O}Q?R?C~b4T՜>~rZX*}$n ~׸d[ߺIJOTżaKzH X_]l^dP;eI5cc_Ճ7LURHΕхߴǭOlVmHPhbʨ)9Z[ځ?>O:=金tܡם$n8#I{zBլF#bHNI6&ǐ N"FMqo~}|{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~p,!xE1IblyޮF.~dgyϽתϽ{{^׺u{-޽6U{/;pR7qkON_>5 T?}xX$SI7T؁o#{[,H`Fz:cgT܃…/ʼnE\-$Lt SX uwQ0$?֞}_WMb+1Qo[?Lu|X[#k *,9 _b,{_u U0R@<_n/^Oϡ;Hao"$2soCnYK6ͧ_{[=mq#s:={liq?߫=6 r޸_?ޭ>ޥз mv #U<:qNQGP 56^ߏ4zzAQN׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~'Fb 0kg9  } `}ևjzG*XgVCϿgxOOOuጎsnE_ר~=x O~O{[ {O6 ׳8ΗFװ^[?O~?)Ԫ~Pm.=ϪR1>ÏWEcr!mc{;[0 W׼Dm`y]}|86=ҧϫz*axae\\'ߋqn  :Zͤެ?OZ8"=@#XQ ~ՎjWv T:bAUe#zt2H?>שu/*W~8GXM QG,#^}o#~ &c {M?~=hW]5 [p? cgԢ`7?^ :IAͪ<ߓao߀^$!B #G '{^o)bc86Jr7I pHopюqڇ3ԠloP$Vп:O ~ozGǨc,mukJap~[BGXhP=j~_O 9}߆:hUaW{^׺u{{^׺u{{^׺u{{^׺u{-α4Vލ˃^zUR*ao6yǶ\Q?6~[Tur,H${H:A0~YQ4 zUF- 2l5؛?[}>u>aߔĨt66 vuG%죪eU` O[>ӥT5i(+p.Ru~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^}J7I[u{[ʈ{&ԟ~\:.-?Et=zyWx_Q$$աi 5"ǭ럽u5\JN*xOu}~׳A"}C>׺2`HAz/~{ߺ^Dq{ZzBU}q~ u{^ A}PֺP!AC 7u=ajW7s~okz[\.A6}8'߀ںq F~O&׽9麢RI`n,Wz5;A* 6&>ׁXo{Ǔe 7}"}]{OPu@θ o7@}5os~n?׳u9uH؃n>s}z{h:PG'?޽۪}4TJox??Oz4[))[e$Ea' _H7mCu6 yX׬ ,ڃ p_YTGMicD*tj%x ^}ꧫ`pzj>]7"܋?[ƝkHzr'/,tqaϿWPu(nyX9G^ҏZER66 7{ҊYt(+M}Wfh"E͹Gubs\:T\[G_z8: s[P"1 O Ũ(eϧO?uL4 xx ӧ ÎsjlY@ q^u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^^B=jÏRccm">@]P,V$(zuOJmZP~Ч^@'G{ߟU=s ^[aߍG[=5?=Fk^Hf'G?ߩNW:X "=WW ~=ʽ5^+ì׺u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^y Ӌ|{^龩2x[`*)֏˨I,Z?oT*z6 NS2M7M):o Kq??SՁX [>ucc??^֏Yl&^@cjjq{zߗO~zcde K(lm~I>Nk M<_OW}@>$SL *ַ{APIm6~}kM:x)*rhH\~ sGgyq 1ߏ}}oIQċAޫ鞲-z.?|^O*F@ckk^îx?uW句?Z4zB_{[aUo`_~T̡O~}F(~QEuꞸ|p~޺?6}u끡n,Ňq~ԓo~^zqt SkMc}\,ϩmv[Z{םzا;G"A1'P k=`׿.>kɨz']??>Xpoʄcj24&,?ߘyׯTc9Vz]5D@1Xz~>^MFs}=o{^׺u{]Xʑ{^R*OP7>@N6'MXlE O<}l? z>c\>_=rx,9R4s{ X_Ɵ-8 ؒ=(4 Vdt#UP7*Jdҷ#Ȯ`svrH$wN;0cV_ ^}^y]xay䆿m>O^D9k>׺Q` Q2E _vUB4KE?!<: 0T?<ՆzpgMDj ^}V:nJ#@Aǩqv״!h\G'q`ߵ:_Lt_c*:&/az~{\kskmǿW6j$1.  }X)<R3H'l 7zZz} '?|z(u{AߋzR: n?{Z~{ߺ^׽u~{ߺ^׽u~{ߺQ*:(?MDpH px[މ[֛D:Uoβ}F0 "?ԱA厯^{)պ??{˯ߟ~"PqomcgO>ָ |!˜ɱ*e:Ͽ|^:X5:mo.zP}k{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^YғEsvP]{˩^׺m87 ccsN8H sװߕ:: cϤ(T>LWSө lX]$\ߎA?EEz:rNGKsk޺ˬ$Hqo>:/~ìc6Z8LuKg]ǭ[uuuӛr9z6{^$07%nx&׻p?>ױҋUu}@7KӋߛ\?Q}GUsx{:#0ֽzǗ^?Rd.A%x G֨)U^׽u~{ߺ^eQ ?ߺ\?cm ?.~ߺ}-pH {^\ ,.EuQBok.-k\?pߺ]^׺u{{^׺H&8f s s}Ѱ*<WL׾PgqG_˫p]͔(WzU7[~I+% __z"ugֿWR\0oQR"_ߏul/JLFyYAOw:mScI*j@'p>.+U=*~/ȿן6S=9Z ~?^9׾?}Oïr97[8qLtfJAW h6~=VW 5өlʼnk{=zAርRPC鹰 ?w{ǫIo$s6uպNۧ@,xTy}k~y7o˧Q}Cq_ݖKy q!%cbÀM[6pqy`!YHakgzqH=:EMDz !j6}Wz^LN)X,(b?V_O<{8ǧJ]] ̮oWkOV:u}2 ߨ\R>4Xdlo?+ٯ^]Ht rXν·}׽?j5KG.=uV}A fp~PSooyM(AS߿u{{^׺u{{^׺u{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u(`Tu7q{#D {^-J3KLT$B[}z~׺oYf$)oq|OMdi`?>|= u/q^x@&M$n.??_uqSu N8\mOϫSd׏ag\x~_޾ν'B.=Ksuz˥AæjD?RnN}?oQ@ߏ;í֣=x8?~ԧ^?k~?y'}~On:nHoƞGIߏVQ޺z?7s^]uA ONkqrOӓ֎s׫& {}lSpE~XWSׇ\Aߺ]^ p?xcMkC p17"ʼn{[a),m7A׃6>?ڊkpX?oz*8u`F|DCrQϺ׵Ncbw,CiA[=۪u;&!T)?z5iY"Ũ i~f?߃k_5~]w^[ry\_>ߺz@:ߏY_v^xt(㘴-{BI#6~]&7-:/,~X OWLeoR?ۼZ2x[}:^8Ђ~=ꂽxzu.QdOX" P= x] 1%}n>c~æMzOHuӗ(X*<[AKީLPŇ&^ӕS˯uٷדk:]EƇR(}E.ſ~ GZRM:֏1Ҏ}C"B.Ҳ{z\㧺}F){\ȼ}_.a:Ӟ?/>=Tփ"pJ@7xcQǪө` o{tUA GMoutJҳ)bF?_u ymF'vhT[-ߴ[:cn@kiZ"ǓaZ<91KPޠ ?|Gʼ ?lN2sq:X5a:(C6ެ^r~E~]4x )A}o~r׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ]XAjӮ)䪓z .zZ'uIGO-͍ߩ#_`rI_{:O3A't[ہngWӯ-,AS7ߩӘy6ǭtU&Pָ"^(:a-ObŁNQ^=q#IFz5}~>*/ozpo_o^\͹ߟZOO]l;\">Oǻu^^׺u{{^b7[߼sN%$ 6?ǽujtTly"Q>ӪTu9i_^Un":ƟW}<:'=-iI1XԐO 1{Aí=@)0oIk R8#cÎzI,;@P5Xpߟw")]%yd'9^Au+ׅ8_8E*k~?'uzPƧ ,T`xLyR8@\Z,s{<=G* 裒EE 3 : /Eު=zކ:bب# rHMϽVh2z7Z\snC[=So/rx}{Z$zRmAr/o?Ozt=H5A/}kSklǕ6$޽꙯[Ռu L lE F6UR*`>WWA'E~˦ٶRtTߞ9 Mi^m%Hcd:6{~VXt*T k_}{>znS)DË\۟=mI&)X{2؂?Qב:㎱scSpx86%Po{~'5uWϮQbuqbT8#{s.Ez5X`q#?>G?aQY"k8 UF@p?sӢtaWVַTqc?@H'Ez]8[L/O?)`⧭f>Ac!A#aqq{֨7c{ƥ5z@.~owM:l?tAm.'M\"YE*:% h6 _ϽucZ*.CX\uICr'=b Ѱu[,m-[zuTd鐒n=ln.nG7aވ{‡ Tj2߂.?ZQSnsI)G_&A7c{ϭ'Y`ڋ5zqA1Qk}/i\pƙI= h҈FVlnO~zK'-ɻ- ӭNZ׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u#TׇQxBryp9>׆zӋZ~z[^ X~/~>QשA^׍YnEPN^=H?E3?ߩׁYi7[mއZ'I"-}x׸8b?Q)^zէ\Gۛ߾νuoK#޳Zu.xqk}y,Iê=e׽u~{ߺ^ f+]{Ϥ/sk/սI鳐 ʐo؛a==7Okl/Px6u˂I%NcGp:RcX)6&[qg.{zXX}zӤY[3oKrnB?_۝l~IP,"J?<>߼ՅS׏ԧP MxW]ʿl.U\"Pyj |zSŅpH҇:\kH.Vyׇ.d7 T>@-~1Zy % /k ccr$_ `MU<{?oV,kCO\Ȁ-k`"~Z$y3Q0*K ~x~CWbВ_?s)7٭q/s h:)p3r,4E{PӅ>uYK-z*7*\I#쎵A玜by76-hgJ#uNڴ+0\{[VzjnSGd}1߾gW9'IVM%$m=x&p̱r8<έ*$1ƓP/* O?߱L=Tc^}+j%զDɨobhW@n/Q`+ ^ǛRxΝ: O: >6ksq[X }EӒOP8޽u:uп<r>~.GS׭uXx=ID-`NmŸsc:s|`Nnf}KqOyxLklW,$~AlH@^>μr8nns_߸ZY[&ֽ7ïTp`= mk}:OV 2(KlOթ_m&H7pMzXQ`_-1,}IquO{PF$doI #zj"2Bsr+>ǧ:t!_")n?/Un=;Snl#KH)#P܏u}a$]nnT݁Z)t{ޖ`9@ Uqk7_ݺ\tp0_?R.kN:sJII51IPAѽ=ө@7 ׺{{^׺u{{^׺u{{^׺uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ\_7{^+Ž#ӟ~G0-`=k SE~?ۘ^C^,EnG/oxcTk3`_ mVSZקZsx"=soQ<cMXp?Nyzr6{t/O}ǽuq@sJݫ\^+S7"`?+}/ӓ,mpQ?:?H#?tNuB9ϻyuO>V]/Zߛ}zPakp>ߏz[\{^[}nI ӯ ?:}77{ztO)0Xk߾}Wr"BɷuzIko~^눯_fױ:nϬ#ß[ު^*OmI>ֺ=uI<>ׇˮyyu>cSou!s~E}NG'߆:\u~{ߺ^׽uQxk8POՍxjTcE TX#'I7-ߗ:ä"‚@lHXE~Tz1Q\W#@oT}.qAc{J5>p֟>.F_-ީQ_}*866y*:(zB̡O{|nF#4$_ӥ{<>[6JHmnlA~}zO~AWSӮoqs\\zn^Oyu&p>>~^:Nv􅰱m}#{nHj&Fn-~x׸meB [8 9+hX'b4jJJW|pV OfPX%m{n;hoq|z+`@ s?O89c/ۇ :_}XRX㑒ib.~>_~Zz|V:&q~Z" \ĕ#E$\b>GJԐ:j_kuAzo~}yu2غ_{^S&ߺYé{WϿu{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uF6UkuJ>xu#rlE{څГo A^781 _}Gj>~}Zs׿p pn>תӮm@ߛ۟V=l|z1o#]k'mo78I V]l&?^î_޸=xߋ.=۪i7z>A_zyX~y.?y{׿/^摯X׿{TCob:l#ְmnzܛ^ b/_ߏZ}.8x*^}y6ak+9~EոnG'oo>?߾޽8ͧMA u﷧A1N>A鍉E?>Aվμ@A~[ߺE:&9^Î8ֱ߳%}Z矩6#ߺ9eS~H =pǮ:Mo~C5%e6#}lM:̕10qXNu,0$:}o߿ue?FS~Ouz{M mAo~>XIMu=a8 nlEZ9 㣰 ʣw}}ˏ^ˮpRJ*UVMXZ~\6PJ0c~ֺK΅fJ ?OC݃zj)CzLGֳV}H{OV}buet`,oq+nH<+TtAyu ~@mDXܞ/o^}uBc ؋Z?{{r::\QRE: s?}z]/ XluǏntt`H%ooHVRHWgI!N9?N?h8[E MyuԀ-࣎*4?oyvӧ#B\s.ױ/`gzxWzr)K)S97]Ȱ9{sNJ )%?p[_Ͻӯg-EC) R9U} -}IpT)#CZ~x[՞?TDH2nK cP7M7's>׏]_}G{[u7$7 L_'[O[zp#RV͇9TjӮN-E]H[}ϼz3t d+?ttҜZɵoí_?F3,x立j8u:̟Nlmos{uϿyufJC#?kݫa0XŇ=W)7 ;<{T5zA!k~8j:Ⱦ+s?>ON:M]#X7﷯PtOp}/tPH/b?}漢tZ0?ԟԕ[A@A~w{{^׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uՇ[??ߺ]hO+$~x<}9׺a׺*y#d[/ߺ|(^ߎ"oz. A{}x׫Nn~ֹ׽6xu`E=:rVXB~߳L_>l.C)ǿuǦ-or?>m]Z]i",/s~߼ˮA??{ç##zoݼXV6r^׺lp@?SkT=t !Ox?Kjϭc?GqߺХ~]:Q/A{U95h)ОH"o=]{c@??>na^zp7/߇Z='n-"c|8Zu|qnA ?^h:蟧AXrïDj_Us>LPkNoIo^E=ۏTæi5p?x:X&?QX!.GЎ?={v}ICut@ XpxX~8#߇Z5\kX K)m_Ãcuˋ .m=k=z܎?O?}ֺJEE'.u:}ƭ۪tQU,o!cqoqޅkNʟ,J__{8`Ӭ(>6Ůy}=kh0@uja{Zs]{<:{lf1q_<:8ߎ3a׽u"[ }( ߾μ x}h@c]7`>JtE[ߺLרde<*ZUO߼Ɔl^2Xp4K a?|ެ:- JQnN,T\~ԟ~ MsWΰi@Āu4C~??Otn=`"B K1_EfG>C_rcPG_ ǻ^s|±&P^sp-{J($*I6 X|~ΣV:-p'U߼[W?aoZW,,OmǭiRG"N* u$~??7j:MQpޕ7Pl[{U=N׽u~{ߺ^׽u~$!j@loz<>άrE2TJUVaŇ[>ǭR] *$1<}97ZnꧪabȿK7arWa{DPtun_Щug$@@,<o}kM07iap-7?_NA* 5]Ij-co~`M1Q\-Ud~~[Պ dRpCF{n/}֝_PLlʄ$_X^_t#j:ho`UN_[xWuʝ.vN௩ͬ_~:ЃRhRPߢl'\uQMDu T >\u5$>եz#1< OVXǯ/]OQ>ׇ'ӋplԄϭy)~ o 0T\T 8p1Ӎ2$2_ tNF&׿bG^9KO?ԍ_^I޺='!cp?ߺtbsom]{񒶱_[׷=u1*~O}cIQ?B{Z략x|={ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~uqGu^׽u~{ߺ^׽u~{ߺ]iSQ{O^돊1E߅ǽuucrא}^\5Ҋ֩ l9yB4L>f}~Oz[zrHֿ6Sjaiq{_n~:|E%}A,?:>HRɵ}xq n,EǓ_F?Uuuqo߾ޫǩq9 O\t~o{꧁>ԕ׺㤋I]~6O~£|ƿ_ǻuZtS+9k}OߏlcDp {[y&ޱ׎zk ߛ[߳~g$`G\X{5OYU%^}=hSϦzxE[#޺ߟ]/kS"@ 76 H$w޽ש)+?ߗZ_{W y"y#I}>VL\`)2oVҷ?Oިxu+ìT4djHߍGLtSp?ֺߺ^y*ozX_"{^zkʋ%$] 3j:g@Hޛ_MEz[3e~@_z|E2bGF/{f.WQh:Ԓ.o^N oȰ`8~z-^ʤq#}}xdoMI +jf&/ߍN€ӥ,_]L߫P{TcOzu{{^׺u{|a|E]#?~}ΝlzVc~P\\}xϺA?C_UfXdFF?ov)3\pGOhej*FVFm}9צMpޠ5{֟\: +!ܐ?'ɮ:? *n?lfgpymȽyΥ/q=ϭ L:a{{s|,\lRo~.@ ThqBe% ߺ4l$_~_oߺN?~}z6?קR=y%mqu>~ÏY~ǮW?Oߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u.@1 ݗ^s%i6{iíPpdY[:8M-n~~=k ө} 1<)R(:#1V7}}|LYVG"F5 .ޏz:?6B$ugvS0Kj)"ZShy5+bG{}?߁^#5\6#ͽZWJ. l~ުz"7nV>}c?'>L#?B?}j+0Aso~U2D"iK_CO2?ߺЩ\7~eox~rO${<[׵ǿux"S$o}>H#Fԫc?{:Y}t%ÓOz[uJ#:ziE6QLuC *AapMz1-<-ctZ@[?7>kϨDk\8uys{H^׾}u#~8}Wd ?_ǭ8.K\ؐlEo{<:LV?q[|Z/ױ½t/kɸ[hzr BA}08}vna^cqby}o{xp{J2XvU`?}GҪB{YzlG86,@΢5}}bHo-n, q{[u*xSè+ѵj$+Cqz,Kqo׵8SD V0+nEO^}c?ߺ^׽u~{ߺ^׽uuEk_/6MVà5OM#J7n˞I9#~SZN}$O /ko^O> zxTO<}yU>u½cEe~AǽVT҄ #_bU>&_>=QkT"R cuU/*̶i5>I#~lONqä+{_pH&}L@JߋےE޽5}r,o˱r[o~=˯CmQA7MO{kNirђD $ޢ|;z&0 IL/TSdc@CpIb?ݺo~]px?֊}mϿuA1#,ǽPuHmJz {ب=z2Tl90BֹPӤ^1ʦ+ X_#<{)ՕO> %5~ozYߺ~ֺߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^6_~{ߺMֲQc}l}wכ}xw׸uq{ߟ^&u8>I:)Ӭ^zO.mAV׬ o^j{-?\Ӆ ?] ꧩr|xǦ/>okpÒ'{u?"-{ߺOS0?ޡͿߺ3m}իGM""ǿu*u_ɷ\~E?_~CRiPȷ9?>֩vPRE}uo^ҷuUtssDO$X>_.uU&ǿW:n kOG?_{׈GNֺF?֫o\q1}{_ cn/ansa?#{vfOzt7Aܝ<:MSǬUj.~~=mkZt[>~x()gk\~:X^ B}C_ݫhG]X}l""qu<Z*(HFrySu~{ߺ^׽u~c^6~ÏH)!ooU_UpBsӋA2N1C'Qng[bͮVi&JL- ;_}]oIWΧ^@0"9yvq'4Ӎ5rLHq l=E:q=u~{ߺ^׽u~{ߺ\$]QͮP}˯b?Q;]p=8)Ŏ ooztzr/~}^Qߑou6_$^H=.@axċkϽPi~\ҡň,X\=nR:SS(PP7*zwU8,]I u~G#jztƶI䅰>\@$ H~mM?yFe U_~~zHxc޺^?G#~׾grXMs A$}om4 H,IRU>iǦ1Ѝ+"7#}S׺u{{^ Ś$וuzf4lH$_H_~)^^1DHD'_~zMK*h֥l~ǯT"IsIV[zxm'%O?}J+}u6?uN߼@=y#ߺe~g\uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺXb]Eu{^麸 ={oO6~}ڵVy\\Tk{íu"Οs׼y?CSæ9>ވ”G?Q"^#_޸ptu ϭ6'>O{Tӣz[ǽlmcq^'W#ߟuVbP?X{[R5Ë{'>OLN9cz=X^ 9 c{IozgeXj6 ͔{MAǧ?C[~}\8~'ޱճ׿xap?Zl8o[]ZlT鞳pWA]e?P^}zeBlxЂo؁{T4R9>׺e6B,Osb{a>ָg^0%u[ߺzi<'nO䖿uG }7Z׳rF 7sGߺϦ1IeUlmMjG[m&nojf'ީ׵TK * ׫\Jn bl8'}[P]$[o}SK"9M_{}%v@TX𷵇>׫cϮߏ^]^\Wfu3*ޫ^9{(zzx׽u~{ߺ^׽uŅՇR?ۋ{^# ^x\yߟG..@*>sW#?_}t9~E{9P{q^qWc ؁b &>3כ~"?|zܪQ'말?؟~zބ_NL$sKJ?svz_7Kh-fn~>}f׽u~{ߺ^׽u~AH<G<{^'M.>Eި+t6 B`/v~ M3`*]@7+H[ )tb'O q1^4eXo^é8͢Ab?Ͽ7 > VHT$h%O}ۦ)Rkq~٥[\@87^}8>]qx$q́^G,o~=>@:l}{{^׺u{{^׺u{{^E~gMc?ǂV?@[^XO?ߺ}:G?}=y)Ou=f׼οO~Ǐ]Zߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uHQ }-}gˬ G }L8pOy!\s{X9cu_NSזH 鱿u4G^StSO1Pr}?gqko=lhP #=hRQW=iӻ.J}{ b AZռ׬?Nx޼G]qu":G&~?}qDt'#}>Oǽ8}M~o[r>C5CqG ϿWz}T>~}?Rci  yP{W&ߋ5og]zmfak5͈-_߳׼/Ҽ_Oݺrzj[N ,xUן[>΢}/b c_3Sc 쎽~r?=tC{&X=zZ?n7GhqL܋R͸'?sZ:rx?{_vb_޺tۏv3&'ZyןVa OБcqbG߱ǭps8?#ljNx7@'{[@0:^ 7'JדZ y6>׺Q\JP=G\{^\á2./{^UOu p{׺`T=t%I`nXnMaTdq? E)@:GQeB~ ׇ^z(nJ~>o>}8Qǥe?}V'Ǐ~^T^׽u~{ߺ^׽tΤ7>ZOIcGV_IV$}oc,H"mu>G@ _I{_gVԶ pO7{[W?o\qԅ.>TW\FU5[~^5.id(W_്_ns^^׺u{{^׺u{{^^>׺Bb?#{^,y7[ߪzME",GϽ׏[vjW 9(0Q}5z4eW[P+nOSx:oĩP?{zˠK5A45Bbܨ/?|:u[?V_~^{ZN?~z\ M׏Ϧ,(ֿ"瞼xt㌗D }K[ړ_כC6Jpu\\>4kNzu{{^׺u{{^׺u s~Mu{WӯcϮkROu4l?~xćߺX-~pa{x#ߺ]?Gkߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ\K(*?Ͽu{^ȑNV\.~׺2.H?u2* /Q׋( .G'޺G][׺u*{APyN{^SDĝ6$ܑoacWnOoz>]pOn "[[FASr ֳͭ߱#^<:hK i px_z[ #O(!!M3~ ܍6V{:q^,rklA_~z}:%E-ǽL=1̺fu'<bZ[W=}xX Uy:Ѡ&&\7R>ևί#puz:}7[>׸{?>^׀Xޅ:OO ~.u[ݺ}h$./.~O=yqAzl~y??,Î [ߺ4 UPoq!ZßǿW8t{F:MUIb6=:|XxQy1S-ַ>ïN-lmLRAoTתҾ]Ny$IA7W߇[<:v6U$?ߛ~/k^O\"9$^^d5~=zϮwʼnF ߺ8DTy~~{{^׺u{{^׺lZfUJTtpf$e}lGpWpV uе-H6so>W\AR7]Ϩc?Sfx_W_\Mn\#}꾧k_@@Prx=[VdQ/~oO>>={Z׺u{{^׺u{{^׺u{{^׺u@ .^骫M17( m}}=6Oi[{~o﷯~m,R)PG@Rzzq銫i(&2WWc~}XWɻ51HQH~Ç[ 7SEVhdZͦEַTu'˩ II}T׫q n)oJ` Y,M4IGm{:{{^׺u{{^׺u{鶢AǦF#]@?F z}ge<?}kՐ=_>שĨu ߺ\'ߺ^ҿ~ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ\.ߟ8?~A2p/soUKsE<:O<܁>N垹(&A -׫*u*p \^ݏ8tV?M6?Mk[#q?teqi״zOrn?~}zR ;Ů m?{z:yST.ݺPXݔ: {^] ɱVQno~똭Zު:Y ?Aquߺ\K6,Q^H_P~}rAs{Z=tU[˭p뗿u41"x??}gˬ Gb@P@$qk{R:Ko-{i@XXڅeߋ_}zWOlmOOּZntp>kLuǦБ<{?V'=uk^_?t8$~]{S {p>Džzwj~ >GU釛  ?#޸c]^8?^>Zuk"BuՏ Ň\~P{Qk mno+N\b8>>$\}$[η^͸p-zT"2/{XO^N-^?{U"/~/-7[_q[MZ}?>ϯ}?П?>cˮk^T]'bmp}})}KHtc~.~{ՁM[O_ϿiuņDI\"!Ti.+qaǿuԨj0גWcȤb 9{Zz4.\މ^VD}ob6{z83?>ֺ@H:ZI6#ߺY}I<~>{^뗿uN?py=էIڜg{o*J~G+Ճytq 6X7Acq_/yuV6a`,{.9`umXpFPأoŎsa_oJ5(zIGX:@[aǏ@k{?>iZ#CXKA-CZ?Sg觸T}??Qz֓ӐERHϽ洧ZfItcf{_zY|2Np>Vny}xuE<ni$4~sq`uwITѢ܎CqzghX  O׺jP&}Eo~B^B\{ߺYuG}}uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺXe`5\<~f*h~ccU` Co>Zo~8^_G7POПUz]w6yzKk{ѧμoaA)^o$Zߎn}҂?iݺ-H^ 5Ň>pzq`}^=wo׎=ֺcPa~FiLtTϩrMj\pX-som#߾޵'1$؂<}{zĬ>_׭ tM! xh#ߺ^׽u~{ߺ]$??~brtsavP@V{:eߛߏ~@ M _b~ZicVVMӟϿu=ge OЂ?ۋ~}SJ&$eN~]wr?W׎E}ǿuîr-ޫ׾Cr Kkqq#r*=D?z"ͥo"{˯ qBFƯ6-Ǐ\TdT&}zQQ1(n~caíH$}5OׇIf `,XE{Jq똝/{&{.a4]#y>SZAX2dgBC0KifKjԯ tCid!-q鿦O< gJ%?RG^j;"U?WS gopږ-Ç]RnnK p'>:Ti (bM#=TG`A [QH}xPP=3Z1}GSqX{ߕ:5O ܁{^׺u{{^׺u{{^׺u{{^׺u{:M<RT}'u1zK0`O PHb/kou=oΝHWS5 ={}˔[6X?[7ګh+Ň95q>{|֚tTH 7[|U#ӧ2hdnx}j9p4 -Uk׺u{{^׺u{{^׺u"~B=@:lxʰu*Ϭ~u=gOߺYu^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺX'6~l k~B·:j́.׭s. >}u#r8p<Hh XiKq}W덾_<@? ۏo~v>^Á{ˮMē?{^%?_>*0<  ޏ8g#K؂{8{Vgn7珥~ֺ}1Xq?@=Z-!&b/{׆zȹ7xszn-Q}{렠_-p/\:5މ_CoRM׽u~{ߺ^z^*mׇL ?ъ}ǮJ?^-w$?O[\Kae^obR6k u:ll9#>^GNh0Ur׽u~{ߺ]U .ZtJn?{^1RnJ{Qzߎ/r_z[X n{}?Z quEzsF/K(}˪ q{?X ?'@?^qVM<~u\rG^ ͸kԪLMco^ևzO8+ bC,>8o,ulMmI^\ۏ{׺cKr7pդW&Џ}?.Hlմ-:HY,$ v6{Z4ڔ"KqLYXZOUɰ=4s˦4lFf!8i_S^u:T-pH+sn/k?oZ _U X[~W׭LǕp?F}O' X {k&@r?o uR5=:#P~ߺ^.,/}ue&G{^׺u{{^׺u{{^׺u{{^׺:Z׫NJB5mR ~{8ӤI@Gs"z~[}??\ŭۃ#>!6#pmC.5Y'G mNSTT{6C^>xuSZ(`OQ1ij l=QZ<aJTuVuO/0_mT=f׽u~{ߺ^׽u~{ߺ^׽u.A^XߺXUm{??޺Y=uE׺{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ]2pF^~a2,Vb-{g>^?7K>PG>AzǞm-6+aqtjOE ͮH@`Ӕ Vԋ[ߩLuΊUKX@G}GZ9鬣ʷ$sx=\g\lT7H?׾}O_n9?{3xa}h%pԧ_ޏ^#or,.=l8 ~Oxum}5!C {UQkV_Ksߺ>G6a6׫׿o{}8|=.}/ߺiӥ  }?اU^׺u{{^O>O׺daz}xG׿u~]x~m~^OH>ֺ{{^׺u{.J0$_ߺtnHkuukWq~}f&R8~zz*a"Y@?ߺY\ǯ2"ȸ>׺ia7rGZAתzQZfߩzԷ@ :|qkGSTy67-M}uʩ5c=˯ צ1Y,[/{Wux["^$Z?pb<^Ǜ}@N_NВj?˫q9,sq?߳׿y%,M/o?=ckH[6] \߸u ?Wv ږy rs^x_,1hR $"޽ϦőZHQ&ߏǽu&cUHݾKQz| _n Vܑoըu}@sr?OէU¾W^oh)שQ_VRI Xt>QQ׾Ε"V$R^E}ۏTǗ\jê_PEoO׫™k nspqo>O˫G:\#%5?{#'OSF]H6WQNߺSůi `\߽^pH%xZ_^Bxu:`O k^׺u{{^׺u{{^.ߺILK}J3#_{z4&/%bzSA#ފ5c??8}ӭU`A':P n?}@X{׬.߾]{z}k{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^NH?ߒA7{^Gc?SAתzH^K{aA?{^!qHď$SּzhzAY'ߺz/'ǿSqmFX $߳׉\+UX׽‡@r@yo{Xqo^>׳uG'Ź#o~zR,W~'?>:yS/{]{ߺ^׽u~cu>`r8'޺^ rB-<}}y={H z~׿uݹj\_ſߺN4G[8ê+ߺ^׽u~{ߺ\_k}t׏L1,~y[޺뎟Ł?כQqױ^`ZWnu`?}H굅ϿgPu2B<,Ha{Z RO#ɷnC8<6~n-dzukT ŭ{~T=eIqb=g,mo^eoc~w{M 0"o*6>jajDoA_~u=`4$ua7}~׫qΐ[l?GSިz>]z8dW#<_ = O~iשA\_^}ue%ĩ$3$žzWzXV\pQʩ?N.yP<ժG?~]_1kx$~?=J_Fֺu{{^׺u{{^׺u{,׺P?_~buu׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺXf_~f96~b1?{hϦs}|n?݆׽=n\)<b-y}==nri$~ḹ#߉Nn?}kϩ׺u{{^׺׺P׺Xbl^i{AO~xx uOߺ\ *~ l=gϮF}/ž~T#ߺ^׽u~{ߺ\]n=?<[pn Co˯~?<Ź`?yX~@I<G?{ח^O~N ǽz}k{^׺u{{^AFt߂b?} 7?[8T'ͅ.ċ{=o]q"\_q[6ߏ>81ӫo&_=`Ip <^I'޺^0yRMTg?Bs6^*[Qy|O)Pnx~ꦽ<{Z+sԺ}[6`=nrZG.-ozYZRUnxoǽF07_8<~rmkm{o{s{4Mg!M_ڋ~O֐zI1x؎nTX[D\sƴzh&E?qԛC<[OӅtn8bO{TUmsH6?OtA.5X(~-=\WPklHiոW]7}l"Aoz֫$n :MxuW lyl.uSԚZR8{>SJth)n܎޾uח52ذ?uӔܪڛ /A/CO#ɩo[$o<~'өY7^Gok~g6܏ߺYApo{2&#ߺIlg`tG`zLK IoO1RhqTX1nIx?OO^:R,o6~y?a}oW]mc'>G^״^mp[ocuߔbJ$e<Oz5@p.zk{^׺u{{^׺u{{^׺u ZW{^ >׺B>׺ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ\$\qߺ\uC[{֏L% Ou~?ހ'=^]{W|'?7~|}:FIW\_#߼~]{ߺ^׽u~E[qq=ÏM-\uo\=sΛX׽R~:Aa׈z1eBTnMJT?#`}x(:*ֽ ~}un~=c4yˬFNyO<}}{-us)VA']{~]{ߺ^׽u~{ߺM+ߎo~7?ϭF }Gp~m>=VzX~=WKߩ׺v^T?1NjȽo6Ͻu9[ԑ{ s?\{:^OOߩ׼ki 6iskOS<@?>~Λ*I$ I{[:o<^=u`XZ&Ӑ}={to[랷Ϫmh 3 ,9a<w#6}ujשVx׺ǩO-=x׮Y@&ߋ}[+{*$_ǿu|<7z6iُ݈K{+ i7X7?q4[& HI7ߏz .4W n9 nmΟ?@y}znEH/??ǻ=P *a:\[{4WL,~_nl?oz#Uߋ% ߸PT boꞵA=^ɨ{Zxz9BOV[}n~^MaF ٿ[ ȷ-?~~oS$SFYTrM׭t#RHImo~U=9^׺u{{^׺u{{^׺u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^(TjbH'ߺY}2X PucOZ~ND7#O>㭃?܏Oí=L׽u~{ߺ^)WRm=u}=u{^4\ry~9Zt?޽˯tz}{u_ߺY"nƾ׺u"o}ko?K~}[\?/uI(1&מ}x,Z^ASmJ{]bR/[c`_7?^^ ԑG>֩1*>׳%uoOߺ\u~{ߺ^׽u~E/4܏׺}?_~>M[ߺ:CӤvжza}kU <_Uղzx"ꎵzsߺ\@??=g}[?CӬF}=`RVێߏ[A?޺^oߺ][$ğ~w{ԭ[Xx}{ʽHqtokϦ xDčknl~OWrIQ`ŸPoֈeJa+ ~ Q׈JCzՇ1'GUE:x@OÂ}k'k\߉bqF)?N ݏ=uֈ[<C{Z뉦?q$O~@ZEYx)ƐI^8 <ۑA׫ӌ<6=uaŮ>XEϿutt1u6 N$?zj [^ܟ޾έZxܑתo] .G _߼֫׸7?1 ~xM:Xt[y1-P LWd/< ؋_߳^4V"^o~Zzb߿TAuLs j\Vl΍)>?߉U4u^/}ߺNuSHKK(7&&Nj[>שנ̣ qש\tV6-OdqSE\2C,xo6<-<]i&Řmp~{G HC[ȸxﳯtPH:EyuZ < qmKCkZ럿u{{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^~q/~AVe[?}Cphlmm#uZg+X>u='N>y^Ͻuc~]{ߺ^׽u~tyc{ a}޺^͹#?8}o~]sB>ss|=WqSME-?ַ|qC״oksSߩ^#?_Ӏ7P{U '޺ីb-[9xu{ߺ?؟{co6+o?K[{珩[u~l?C{"1?>~E306?qǽuuNGןRש/8}kbT?u}u߿uS׺bCz?~׺izu=p4XuQc̗ ?qb}@"ߤ{[)~|Z~~?^׺ [^u!ߺ/{+׺mߺzM ~"AǽuӋ7?_ovhEEt"}=oyEu5MkՁGXy68{ޓv^<5\ur?rA?A{ ::k?qŇbSF$ǽ:ei)ۂ@'_CsZ5 8~o ubgu[~uZ WpG#U ˭c 0 j-bM{|^xu]O?[o{]re ,o׺hS=nDlrZ[ڭ uF|}ɹm~ 7olèFz׏u`ء7`,/ꆜ:zU`.oɷ{J(1H m?U>p2ؐyo:Oֺߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uՀ_u׽u~{ߺ^׽u }Gmu#]o^ۏ^wxӑ}kP#M]o}׏^~7Qֺ0??~X|}u_ߺ]{^TC{G{޺]u׺{]i=GX=c\X{^^7ߺY>n?'o}h%+ߺP sa޼:'k[c~똜Ekbe?^9uY[o{^׺@?=uE" {^끅׿u<_~d _ߺ\\{H6az~]u4=|E|~]sӃSueďxtw{o{Z뎋u`W{^{~ ։]uSׁ߱8R*[#)^=88ԠEߺ=4UV\~{}D[-{_H_}^G\DV!x>l?O~9량-ɿכ}>/~^뱭Fڮ>[|}ytNo{ _u{^׺ߺ\ j_ӟ~p0}uѯ6_7qͽ52GއZ,o{3ӹI$uA^`yAo[:G'$=o4뢆pO_#c#ߺוO^G oGoߺO:V?^H䛟o~:zH]\NTy{T=?Fnֺ*} ~[]zQGA$X_z^=Aۂ~|ϧn{]p2l{Ac{^׺u{{^׺u{{^׺u{{^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~tM?~{P׺Ҫk"<O~dZuQӟuqGq{u~{ߺ]i_?~uOߺX+{uߺQck<{^{:G]xcC}zqË~ub~kϬ3'c{h?{:^~}Ϭ ~-o~Ǭǿuh?kߺ^ߺ^m{Nqk/?ߺ놃}uߺ^>׺拥$~=u$?^!NO?=u֏=Ӯǽ=u/u㎤^׺u{{^׺uS׸ućߺX[\L-ooߺ\Dn׺׿׺8z6w'r?_{tb'kO~^׌g>O{h?sa#=zfi[~8)ouΚ@ǭuOs#NNsyuSŸ<zu_'ǽWg-'ſǿׇNQ0 n=^׺u{{^׺u^D7u=(nt_^5 o?^Q3-ePu׏\&OSzblxy<Puꎹt-{x^'_97?P?Yd}tU\ Ů,GNkk~=l뢿^7K끅M&p-=Ѐ[ߞ~=uׄ Yu=ĥӟu>m$?S>mqo?>ֺ"O~&ߺC#up}{[2T_x~J#u}u߿u{{^׺u{{^׺uߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~tP =u߿uN?G~pxu{^$<ߩ^:cϿ׀?o=d~]Kf @6Z>שBFn=Ӯ^C)ߺ^zd׺׺ߺ\u~{ߺ]ߺ\t^1=u׏yAO׸ͅm{OOտ׿ ߺ]_~euOoO~pxmGcͿ{^#ߺ^}?O׺9Z3 ߺ}u}uߺ]f/{^]H׽u~{ߺ^׽u~cf #^_C! ?{^ =u߿u{{-^=uBϿu18/_zu׸.3ſ?__{]b~M׿ߺX4[>~]x׺>7]{[kuEpE}u#ߺ^׽u~{ߺ^׽u~ck^{bB-{~AxN7/~:)[OuIN,n?~`4.G?>^xQc6kߺRԢcq{r$ߟWo˨ /o{@uu^9#~ױaEŇֽgߺz{x>^T\\O~]v}8_}{Sse[ߺR\q}k(#>]?9p뚆׺ʬ^YC#^Y\u~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^)@:o?׺ ׿u{^ߺXǟ~u_c{R}uO?}Ͽ:׺{{^{:W{^뉌_^뉌k{^)_o~y>׺k\sG~ {]^u~v7{I׽u~{ߺ^׽u~{ߺ\\_{^q]omu^^?x o\ğwF{)~>Co~sQ[u}H{^ . SX-?ǿScZoֺFnr~u?|}u5?O>z?{^1N~O?Ozz]Xz:YXc]luY?޽ߺ^׽u~{ߺ^ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^ߺY}uunx{?O^o~HAֺ >׺k[^o~׺̟O>ֺ{?~zߺ^H>׺yߺX=u~{ߺ]G~cf }uֳ{uj_u_Ou{{^׺O~tT~׺{,{z{^^׺u{^׺u{{^׺unG׺{{^׺uu^׺u{{^׺u{%T?{^O^뎃uKO~>k޺Y{]r׽u~{ߺ^׽u~{ߺQcxu~ߺ]s{^^[o~o]"#}{ֺȯ?uߺ]ߺ\ J`~bh?m^:zAzq#[5!_yUb~ˬֺ㟧Qop>׺⣟~{U׺ʃ[럿u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^DZ@ -7~r׈ߺX^K=u׺ߺY{׺{{^׺ʟO>׺ߺX}u~{ߺ\{^7u^׺u{׺~p׽u~sC~eLG~vEuޱ?~׺u{^׺u{:GE~r׺ߺ^׽u~{ߺ^׽uOߺ]{^׺u/׺{{^׺u{{^׺u1ߺ]׺׺׺Gkߺ^?u=u߿u{{^׺u{{^׺~>zXl=Wu~uo~u~]uoߟ`sou}=uG7o~ԯ\<ߺ:āk's׺誟m{f?nchs~p9Dz}HZ=qaǬG??>ֺ {[=wku`=u߿u{{^׺u{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׈ߺ\ty>׺@ߺ\ty5\{^뗿u^׺u'ߺYuQ~{ߺ^T7O{~׺u{ia{׺S{u~{ߺYS{~׺éu}u0?u^~{ߺ^׽u~{ߺ^׽uŭ}>׺{{^׺u{^׺u{{^׺u{ /k{^u{{^떣u?׺ {^~{ߺ^׽u~{ߺ^׽u{^#ߺ\}u~{ߺ^׽u~fS~ﯿu>׺*ZOz}aֺ$an~uf`5+^_ߺ\J~woz}r~b׽uG#ߺYu~{ߺ^׽u~{ߺ_ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~`?S{^׺u'޽uߺXu׿u{{^*}׺?{^u{{^8}ߺ&GއZ돽{{^"}о׺]{k{^׺̿{[=r׽u~{ߺ^׽u~qo}Z׺u{׺u{{^׺u{{^coz<:{{^׺u/z={׺u{{^׺u{{^?~c[޺ߗ\=u~{ߺ^׽u~dO޼ߗY=u_އV7^{Im=x??=]sO']^׺u{^Tz}k{^׺u{{^׺Dd -5 D  3 @@"?}DyK _Toc141076595}DyK _Toc141076595}DyK _Toc141076596}DyK _Toc141076596}DyK _Toc141076597}DyK _Toc141076597}DyK _Toc141076598}DyK _Toc141076598}DyK _Toc141076599}DyK _Toc141076599}DyK _Toc141076600}DyK _Toc141076600}DyK _Toc141076601}DyK _Toc141076601Ddp,xMDV  C 2ALMS_nxt-remeRb\S"r;ΙSFƙb\S"r;JFIF,,,Photoshop 3.08BIM,,)http://ns.adobe.com/xap/1.0/ Adobe PDF library 6.66 2006-06-12T15:22:44+02:00 2006-06-13T07:56:38Z Illustrator 2006-06-12T15:34:16+02:00 JPEG 256 40 /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAKAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A5faWTzqEggMrKnIqiciF A3NAOgzQTyCPM0+1afT4BhgZRj9I6DuVI9OuJBGY7Z3ExKxFUJ5sOoWg3PyyJygXZ5N5wacXcYbe QXDSrsySRizkMkIrMnptyQeLCm304PGjQPEN/NHg6egeGG/kH0p/zjBxHkLUAtKDVpun/MNb5uNJ 9D5b7SiA1s+CuH08uXIIjzT+flnoXmC+0caNJcmyk9Jp/XWPkwArReD7V98zKef4kq/6GYtP+pfk /wCklf8AqljwrxLo/wDnJewMiiTQZVjJHNluFYha7kKY1qfauPCvE9D/ADC88QeTNEi1SW0a89a4 S2SFHEfxOjvUsQ21Iz2wAJJXw+drWXyJ/i76s4gFo12bWo5/ADVOXTqOv4Y0t7PN/wDoZi0/6l+T /pJX/qlh4UcTv+hmLP8A6sEn/SSv/VLHhXiei+ZfPNponkxfNH1aS4gljgkit6hGIuOPEMfiAoG3 64KSS86/6GYtP+pfk/6SV/6pYeFHEwTzr+Z1/wCaPM2n6hZC40uK2SKJbdLhmHNZWcyfCEAJ5AdO 2EBiS+gPzC88QeTNEi1SW0a89a4S2SFHEfxOjvUsQ21Iz2yIDMl51/0Mxaf9S/J/0kr/ANUsPCji TDy//wA5CWOra5YaW2iy2/1+4jtlmE6vxaVgikrwSo5EV3xpeJOvzE/N618maxb6Y+mPeyTW63Jk WURKFZ3QDdXqf3ZwAJJYr/0Mxaf9S/J/0kr/ANUsPCjiZb+XX5tWvnTUrqwTTXsZLaH1w7SiVWXm EI2VKH4hgISCkOvf85CWek63f6WNEkn+oXEts03rqnJoXKEheDUBK7b4aRxID/oZi0/6l+T/AKSV /wCqWPCvE9H8gedYPOGgnVorVrPjM8Dwuwf4kCmoYBaghh2wEJBYHq3/ADkVZWGqXliuhyyi1mkg 9U3CoW9NitePBqVp44eFHEhP+hmLT/qX5P8ApJX/AKpY8K8TPvKn5h2vmDyfeeZRZyW0dl6/rWxY Ox+roJDxai9VPcDfBSQWA/8AQzFp/wBS/J/0kr/1Sw8KOJ3/AEMxaf8AUvyf9JK/9UseFeJkXkP8 6bTzZr66OulSWbvE8iTGZZRWMVII4p274CFEkR+Yn5vWvkzWLfTH0x72Sa3W5MiyiJQrO6Abq9T+ 7OICSWK/9DMWn/Uvyf8ASSv/AFSw8KOJP/KP51DzNNe2tjoM7Xtrbi5ito5o2MqiVInAZxGFK+qG 9wDgpbeLeUvKRufJFzMbZ7i71S7stH0oqHaKG6vDHI95LxoAIIZEEbN9ly1Dvms0eCMrmRfMD3cj 8z+h6Ht/tCWXJGAPpxxiPjW/6vgyq78taS+uarBomlx3UGjRWun2NvcafKnr6hfT+gtxqLvzkvoo hE8/Nl4b0oczhihtsNuXk6LiPeq22g6PZxXGpato+m3FtKdVurv6rA7WNvaacPqcMdgrkrDLdXn7 wGP4j2wHHAWaG/NeInZl/wDzjcAPJmqKCGK6tKrOKUdltbYM+23xsC2UaD+7dl2xiljzcMvqEIX7 +EWwYWVne/8AOQEtreQR3NtLqEolgmUOjARsaMrAg7jM/o6jqs85aNpFv+eNlpsFlBDpz3umo9lH EiwlZPS5qYwAtGqa7b49FPN35/6PpOl+Z9Ph0yygsYXsg7x20SQqW9WQciqBRWg64hZM/wD+cj/+ UHsf+2nF/wBQ8+RimS+y/wDWe2/7ZUv62w9V6JF/zj/5c8vap5e1KbU9MtL6VLsIklzBHMyr6Smg Lq1BU4lYpd/zkPoOh6SdAOl6dbWHr/W/W+qwxw8+HoceXALWnI0riFkyf8yf/JF2X/MNpv6o8RzU 8l/5LeVfK+oeQbS61DR7K8uXlnDT3FvFK5CyEAFnUnYYlYhgX54aRpWl+fNMg0yzgsYHs4JGito0 iQubiVSxVAorRQK4QiT0P/nIa0urnyVZpbQvO66jEzLGpchfQmFSFB2qRkYpk8j0bzPrGl6ZBYf4 M0q+9AMPrV7przXD8mLfG/Ja0rQbdMlTG098p/mROfOGkWUnlTQrGWe8gt2lgsDBcRiZ1TkjFyVY B69MBCQXv2peXPL2qSpLqel2l9LGvBJLmCOZlWtaAurECuRZvnm00fSW/PdtMaygOm/X5U+pGNPQ 4iNiF9OnCle1Mn0YdX0Ppnl7QNKd30zTLSweUBZGtoI4SwG4DFFWuQZvBPJ+madqX56azbahaxXl sbzU2ME6LJGSJXoSrAqaZLow6sv8+aFoX/Ku9du18u2OnXdusPoTw2kcMg5TIDxYKGG222aPsTtH PqRI5YeHwkVsR97m6zTwx1wm0Z/zjv8A8oHN/wAx83/JuLN3Jw4vO/y803TtR/OK/ttQtYby2M1+ xguI1ljJDtQlXBG2SPJA5vXvOH5W+XNX8uXmn6Tpmn6bqMwQ296lrEhRkkVyOUahgGClTTxyILIh A+X/ACZd+UPyt1vS7u4jubh7e9uHeIMEHOAqFBahOyV6DG90VswL/nHnQND1b/EH6U062v8A0fqg h+swpNw5+vy48w1K8RWmErFLPyA0fSdU8z6hDqdlBfQpZF0juYkmUN6sY5BXDCtD1wlEUd+W1vBb fnpqltbxrDbwXGpRwxIAqoiSOqqoHQACgwHko5t/85C6fqE3nWwmt7SWeNdOiHJI2dOSzzEqSB7j bGKySX/Her/9SFoP/cJf/mvDS2zf8k/O/wCm/Mt5Y/oLSdL42TT+vptr9XkbhLGvBzyaq/vK/RgI SC8AsfMep21iLWGV0idoZZFSaeNWe3A9JmSORIyyUHFuNRQGtQDnP8U47RkQO7b9T6xD2c0mYRyT ieKURe+3LmjW88eZWu7q7N9c+vexLBdOLu8DSIteIZhNyYDkaAmg3oNzU+Lk/ny+z9Sf9CeioCpf 6ZDHzRq/6JTSVmdLCJ/UitklmWFHry5Jbh/QU8qkUj2PxDffIEyI4TImPd+N/tbsfs1o4ZRkEdx0 vb5PoX/nGL/lA9Q/7asv/UNb5ttH9DwntV/j8/dH/chBWvkHzYn52trrWDDSPrclx9c5pw4NEQNu XKtTSlMzL2eardf5t8hea7z85bDXbWxMukrdWE8l0HQKqW5T1OQJDVHpntjeySN3fnd5C81+Y/Mm m3Oj2JurcWwgklDooR/VY/FyYECjjfEFSGTfnf5a1vzD5StrTR7U3dzDfRzvEpVT6YilQkcitd3G AJkqWvlnWk/Js+X2t6audOkhFtyWvqNyITlXjXfxxvdFbIT8jfK2veXvL9/BrNqbOae69SKNmVmK CNVr8JbuMSsQl/58+TvMnmJNEbRbJrz6oboXARkBX1fS4bMV6+mcIWQTbz15W1y//KeDQ7O29bVI ILJHt1Za8oOAkAYkKaUPfAOakbPK9K8t/ntpFktlpsF9aWiEssMcsQUFjU7cu5yWyKKhd/l/+b2u ava3WsWNxczoY4hcXEkR4Rhy1CeX2QWJxtaL2z81IvOknluJfKJlGo/WkM/osiv6HB+VC5H7fDpk QyLyb6l/zkT/ADaj/wAjYv8AmrJbMd1LQfIH5oXnnvSdZ1yxndor22nuryeSM0jt3VjWjdkSgxtQ H0hkGbxS18g+bE/O1tdawYaR9bkuPrnNOHBoiBty5VqaUpkr2YVu9ryLN83635E/NKz886xrGhWN xEZ726ltruCSIExTyswpVu6tk7YEMp/NTRfzX1a30ywsYpbmwk06EatDC0Kq96GLS891r9lCKbeG AJNsq/Jby5rOgeTms9Xtja3Ul3LMIWKseDKigniT3Q4CmLEvy+8gebNM/NS/1i+sTBpvO8ZLkuhD iZzw4hSTuDXphJYgbvasizS7zFZz3vl/U7O3XlcXNpPDCpIFXkjZVFTsNziFLzj8hvJvmTy5Hrb6 1ZtZ/XDbLbq7KWb0RKXNFLUH7wYSxiEu/I/yH5r8u+Y9RutZsGtIGtTBHIzowZzKrfDxZtqId8JK xCr5N8i+abD84tV1y7sTFpUs99LFdF0KstxIxjoAxapDeGN7KBumH5sW/wCa8mu2jeUWuRpgtQJR bvGn7/1H5cuRDfY4YhTbCPqX/ORP82o/8jYv+asOyN09/JDyJ5u0LzXe6hrWnvaW8lk8KySMhLSP NE4FFZj0Q4CUxCPH/OMPkIAAahqtB0/e23/ZPmH+Ug9FD2n10QAJih/Rj+pv/oWLyH/1cNV/5G23 /ZPj+Tgy/wBFWv8A54/0sf1O/wChYvIf/Vw1X/kbbf8AZPj+Tgv+irX/AM8f6WP6md+RfIuj+S9H l0rSpZ5oJp2une6ZHfm6Ih3RI1pxjHbL8eMQFB02r1eTUZDkyG5FkeTcZ2KuxV2KuxV2KuxV2Kux V2KuxV2KuxV2KuxVjt5f3p1KeBJruon9CCC0W0pxW3imZnNyOtZD0b6MKGrO71KW+ls0urhJvQuF K3iWzenOgt2jcfVwAw43G45Yqho9Z1yO3njlYL6NPrt7N6ZezKisgaNFj9YOu8Dom9fiUU3UWiYx 5mNvbySPcmOQyu0cQtBdIDw9FZPVVYvshy/EVBIG9K4pWWV7qV48UlhPezxh7cyvcLZLCI5FjmdW CKsvL0ZNuI+17YqoJo+o2Ttqd00K6jI8Vv8AWIiZGLXdwiSyEyItOCFViQ1Cgb8q42ilZr+5RHmk u9Sjs452t3vGGmiMFZjAXPw8wnMdePTfFK4ajqM1pYyGedeVlc3Un1WOJpZjC0QTgsiOKurkgClS cVQsWu3/ANZt1C6unqTwxs19a28cHF5FVuTIisCVJ40P2qYaRavd6tcpdTepc3aL60qJHaJalI4o WijLN66lz8Uw6E/LBS25tTuo7iNUu71v9IWNTOln6Mqx3kdrPT0kEgoZNq0xVV1G/vopr2US38kc V0ltFbafDBKVU2yTF2EkbNTkxFa+GKXaFq15c6qLeQX6xGCSQjUYYoWLI8YX0vSVKijnlX2xKgsj wJdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVSWPTb4eYHuSi/VDI84l5fF8VvFDw4 061jJ+WFCmdP1eLWb+9t4omDRTNaF3IDSyRWyorgCqjlbGp8CMVQkOg62beV5JEZpK/WbKYo63pc Ul+sSBCUHH4YlTZAN+Q+ENrSc6HHqcViItQ3kjYrCxf1JGiH2DK1KF+xPfr3pgKhDeV9OvrCweK8 RUlLRABG5ikVtFBWtB1aIn5YSoRmr289xZcYAGlSWGZUY8Q3ozJKVrQ0qEoMASXaRbz29lxnAWV5 ZpmRTyC+tM8oWtBWgehxKhCa3Z3c9xG0MBnhe1ubWcJL6Lr65ioVb5RncdMIQUmt/K08d1bSxWs0 bRTRSF57pZkCpIrP8HE1PEHj4Gh2w2ikXc6HqktxecUjEZW4aBy5+NppIJFUjj8NPQIJwWtOfRtW c2D+lGCs80twvqf3ay38V2KfD8R4REH39t8bTS7VdHu7qa7ie1eW3muUuopILgQtVbdIeLCnip26 dMQVId5f0Gax1M3AgeGEwvG/rTCdyzOhXgaVUUVuW++3hskqA//Z uuid:87b94133-fa08-11da-9c79-000d93441394 image/jpeg AdobedY B     u!"1A2# QBa$3Rqb%C&4r 5'S6DTsEF7Gc(UVWdte)8fu*9:HIJXYZghijvwxyzm!1"AQ2aqB#Rb3 $Cr4%ScD&5T6Ed' sFtUeuV7)(GWf8vgwHXhx9IYiy*:JZjz ?75YƖ^ܻflZ|U *x %Q$&(8gysm-mQdE-H ()A>vNwdRŘ厅U2?pa`_?7ȿ}*[80g.׿ S doў?7^7N1վ?F{ޛ{m:p~ʦGV ?˾#zou}*[80g.׿ S doў?7^7N1վ?F{ޛ{m:p~ʦGV ?˾#zou}*[80g.׿ S doў?7^7N1վ?F{ޛ{m:p~ʦGV ?˾#zou}*[80g.׿ S doў?7^7N1վ?F{ޛ{m:p~ʦGV ?˾#zou}*[80g.׿ S doў?7^7N1վ?F{ޛ{m:p~ʦGV ?˾#zou}*[80g.׿ S doў?7^7N1վ?F{ޛ{m:p~ʦGV ?˾#zou}*[80g.׿ S doў?7^7N1վ?F{ޛ{m:p~ʦGV ?˾#zou}_N+?F՛+h}uuTuTq&[}e@(l\(G6]č ,N1?8VɒX\QIB#X׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^<|wNi㬏f`}ٺa355VOhr׵* ,.CeF:mDQ~?\ۿK/O׿~?\߾_Qϯfg},/׾/Fϟ_)mΞ\~WwM3UBj*j1xE|pyށTsH]%Gœq}ӝ{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽utMîL?loÝgO^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺFp7w*]??\~'ӇѠXzu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^+ Gc-MOeFڛpI;:CU6Yg֑NX = >}h tvFz*xf;pk_]NC+v42BFc0*@ (8$-}k{^׺T}:xmx ۳xŷ4OTWI:" cpdpU: 0z$=}&wl}i⣨Sn gxifb(Rz(ϲb(Hj EzU{[׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u>?_|P=\ixuo;{s׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ _g~p4}#}a^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~}2s岾z|'碞{{^׺u=+2s?^ɟ?oF3z^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽utMîL?loÝgO^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺFp7w*]??\~'ӇѠXzu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^3~-d֛?g_8g=~{ߺ^׽uq_Ka-Lz6_} u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{OWOx/ mv?Nzag:zu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺73ÃayW__=??_HrXC׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ@ɍ۷'ǎݛ'Ma|[[W,pB+]uQ*F7@K8תI<)E]{ߺ^׽u~}zWdR3b?~ލgBg{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^S.=/%?LJ]ӷ:Ξ{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u nUA*qOjOA7ܱ~{ߺ^׽u~{ߺPr: .3#ECU!b=lJu{{^׺kfo Yp^C5*cfikvQ)dIk;t>3#,-NU<_z1FE ĒPO[,ߚ5:mɻ(1٘bSAԽ5mݏ éwH$#txOA'?Žӽ{ߺ^׽u~V@[xȆ/3S0#ZǼ*dd 0Z7I!IP^+#L'Zn_,jpnO]Y{'vaع1\ErJIO *I!ϳ`P(!2HMI='?ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?N{#z"J{|}Ɏ5e:R*\CM>F@cēok{^׺u{J̜_F{ylWgѲ#L^׽u~{ߺ^d4O}-bv|IUCTzK 0K-W]"Hd~V:fi<5z3t|iSkkdi~2]Gb8]9+4E~tܟf(#oIg~JCw{GZ޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[Ւ/߽Kۛ/dwc~}1sp6nͲfqrnUTG-UTSCQcYIAF=F5Sz_׽u~{ߺM;;զ? q,fP:,V"lB@R܀ = =x i|ջGwNbVMv_LՖ<$2:Rn)9tTLqt*o [spH^3%?!F<4:o[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz\u?c6[I<3}n ۷r>8sctWe^$`M!Ν$ތQQĒPO[/)?x]Wnc)~]5v!'JYN^(ݝw)ųG>/."M^}mt^׽u~Sq`[.]mQvjQer+*imB ud3P糹 dYaZZhSootyLbWQr59lg}LXG<ȩi,BE$q@1Gޚٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uooW#7u{+9mw^MG|f&J]#<=Á99Zz*'ze%4.(GJ!h|^t{^׺u{{^K#CWP1TUYmChJu{~L #7O%.Fflm qd7gUIftȒC-KS%<ۢ [&w8$/Dߒg~۞7O/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵{ߒg~߼4:?;Sl?g^g~JCw~G޽Onѳ{[׿ٝ)y _6w_OkSz3%?!Fxi#uoo^fw7y߿ ?~νO/;׵*vw/ 9G.QMMISoWɂx[ތQBu$CWNt: gil,;)1q5>7%W0d0۳H7Qn* X<jQV˧zTm6[Gh$Y!;_|ȧK1O>rZjy JR83G#3zI|ݟ06.ؽ-_pJaꪷ &.-3F7SXh)sAxidJW00T+A)Q!{M{ߺ^׽u~{ߺZ39>ߝ5՝{9ܻ6\6;oIAuPYZ:h1jD-eh叶7Ԉ]$͜1)Qp,Gc:޶Ru[`v}հw% &.%6j@!9SOK"TSvvF3Mg~l~yqT>Sj\}qƲUJ-b6B2i_/j>>7?g?B3O|7[W6:~^_Wt??_|P=ⷼ\ixuo;{s׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ _g~p4}#}a^׽u~{ߺ^׽t|f_!YIV^VRC+ 9vO}~ξv~z*׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^cO'~_(h8/һ^-u'=,׺u{{^ꗿ.'{|Si{>e{{^׺<BYaHM,EvF1OC4d42202o!}o짣>{{^׺uߓM_!9wO}~ξv:*׺u{{^׺u{{^׺u{{^׺u{{^׺u{?<|×W⿟K-x}_^׽u~{ߺZ‰O[g/ZkI׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_G9ԿدdeGЙo{^׺u{hD'p?}_zGw?u$׺u{{^׺u{{^׺u{{^׺u{{^׺u{Nu{{^ =domt:'i㢮{{^׺u{{^׺u{{^׺u{{^׺u{{^׺Cd g/__o)׺u{K@R16/﷏ O" W^{{^׺u{{^׺u{{^׺u{{^׺u{{^tFR'?F{ߺ^׽u~{ߺ@ɷx67h䏣aϲ2Ad:'e碮{{^׺u{{^׺u{{^׺u{{^׺u{{^׺wO9zu{+ׁz.~{ߺ^gzǛxuhuO^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺW A}ҫ_m쿥{ߺ^׽u~{ߺ_;OR!9vg ?i ~׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~_g'WoJ~#ugr،)g4c֩Pie,q#!ɫNq_޵ԛ{/Ύ;s?;av+l`&Y;5Eڧ cZT`Ϋq~L|11SA/fo77l]?r!-|SSo화mq۸^ xS\!&垪CBDKdyuqTw8d?>OQKz|.t}Mî}L?loÝgO^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺFp7w*]??\~'ӇѠXzu{{^׺u{7WoAh🳯u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺?6zv+ ׋~]m K:u{{^׺dK>kt?ϭ8t{^׺u2u/mM}9}e=~{ߺ^׽tj 7|cUuqW^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~m ;'U YklsJu{{^׺ևMr}G?w'Z{]N{{^׺u{{^׺u{{^׺u{{^׺u{{^׺8Υg{&/>΄u}{ߺ^׽u~kC &>ˁ#m;h='^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~}/}to׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^&/[?}>ΗZ}O^׽u~j] A}}Z|>ΨOھ~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺC/)O?{I7z|c}k׽u~{ߺ^׽tj 7|cUuqW^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~m ;'U YklsJu{{^3ݿlUWcZ<:{:觯{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{J? ]U~ζ_޽{{^׺u{)Og{EOU׺u{{^׺u{{^׺u{{^׺u{{^׺ue+eg{IwV:GKz$S~Rw.ݽ}CB'yl14D8Ռ&LF4H+ @)zm u X婶Vn_?+鶍|y=?7,jtd{# G .s$JaL{@`-}8_W%Yg? Og{c|TuAtrcz7γ{^׺uCfn]3sdǏmMvo/Xt.(eruogD*"('VglOF*J/@ϐOՕusb`X: a捕 +jݬtU$jU?|Oe9l[/l>jYO8^mmm~M;RA'ʺj U=Gfx6M˃k/nA}gվ$tjaAO. ۺ8۱nvxS?{ ٞK#\͂۰SC/$b}kRt-FcFO%5/X{Y7 go}%.Rx!5'x_%Xe$2AA} M *8t*u_Fww_ ԝmt,Xs WYAT$b *.^{ W<$Z mܬUwq!FpqwJ]s-G\+?DXcn!ܧ:U[lR5kz ݸrVkǼt~NbznDph-h; p>b2~]A޳v/p>?gC3d3b~ʱZNdnm' l=n/I"G$ˇ.>-c &~_ϯf?fAl!1f6~xJ*([B j> VtN9~(3R:S9L;e':xdH%ۘJn1 P3>4mbW<)=7-6v{|L*i]ÎI5c=\ܥ[[(ȪdUg%u"Ŷ7[qWQ>S'3ʐ\6IPoSkj:}g6-Eh[B~fKw#'}=ۜc w OˡE- MSޝi AE3E tTz)䎕@~cB ȋb܌u2 0 ~U?oA7b#W4Z}?oIm>IPm#*c#[\hO3HĪΪ5no9 {͹Qtu>m6ovCQzqMCtS=>Ls&kmp?е&|C뢟>j$ox齖 z 9|/Δxq+Zָ<~~Xb:A q ecPv6ıD9&WΗ?ȟ֞;gdS󥧂AĔQ`+$Gjf ~r64mؑU2R1@<ɥKnb:`$:"mM=^+ml̂ O%OWSE25dV> h>؁_۵(dt3~nn.i_@Ӫ@du\`oTmndn:A|&dp94Ny*zi>9Ts#vMc[9zn~"JF$ʜ0:6h߬q.[Ȳ%}5) G{zb6 uWcqx9>; k6DԠYU$ ݶu[} ,ƈ|G vPN \(u<ҝ1A TkFŠ@X~LQ?qT1Smv.uVjh;WU:G4𚸠fob.]m&²[@'LO @HS0:,}37xvO[]pYd0C2FHA4׿S|&R[s+)0= #9]?Q ˲Fw_"׺u{{^׺uߓM_!9wO}~ξv:*׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^cO'~_(h8/һ^-u'=,׺u{{^ꗿ.'{|Si{>e{{^׺<S˿$d7Mt?ڏg׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u''<rW״7Weϴ]+׺u{{^ZQ7\1kl^kAwI:u{{^׺u{{^׺u{{^׺u{{^׺u{{^ҿ':[#l:=׭~{ߺ^׽u(./HN{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uIѿ^׽u~'욾C sGU|=tU׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ[h~le:]k={ߺ^׽uw(W?aiW_:?jK׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~_><rop'auKOѯ^׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u''<rW״7Weϴ]+׺u{7v᳞U_=h뢞{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ue+eg{IwV:GKzu{{^׺uv'>Crs#?~W{^׺u{{^׺u{{^׺u{{^׺u{{^׺Os {%>ޕZGme-׺u{OWOx/ mv?Nzag:zu{ٿ+m%;J~) jxGrf7Z /,f_,0{,܀. KKP^i$1dtTcu=ٽ?yߏt JTrV$2ICJY7aP]+E.d횜2Ŋbi7v&_y$aކ#FZqtwsV|yO㸠ʼq{u܋#|nT S=5n2daX'Dh_~o/s,IKkُ%\Ĉ૕jx-!e 'G.ygȭ6,_+ OrIcRSA4"~I46(ԑtAp S´% j٪`\ԯ[ݫo&9[_/DyP0`4iŖM~vsWgs#Z N3G'7UD]9]=ָwfXg5ۧ7"DG,UXA4tT1 u?gXR^sofJbLwniќdq^;rq _XڠGz?̳!-TIQt] xV/5)<ǗdHӢ&/&%UB$]٭)SOz&qwN#O\ve`%ܛ7{,23>K+gmڳhbxܓb=v7qۢB0RP#.ҟ!SYWƿR}%cjzFlxvW_ME-Dy'TR!_xg/ZK{uqoKh.bH%*TL^-È O2Iܯς9`|VĹj 8VJ T0qȤY)%Fjc+"mNى]p1"U&)Td'{Ϝ˴ν$Դ0`%`UG9_ ~x{st텗^% bmZFncyECK,8URI^=$92s(l 1P);cotsrK9vfI',G꘻GctMUu7J>G- fܽ_XHMrT,F9oD~}usLv񟟇wg^| Ȏ˸&"XTz-<],ȝɳiIҞ놆v,!0Nb)ww rI-ڞĬ`W]_0iZ `#sCʌ>j?ҽZwQO,7l||IVK !]_S'@jcڠ.H3*pݯ+927[4eUR<&W{yMxMm\^0|7>c# ٫g/Ɵ{B9:Y%XHGy;^ÞRmN?NwDDՅ@T,%\܄+n\iX4j 16EOOEѿ?5O\[W/Mu%bz NtJSCž϶Iu8 @=M@Xɐ2mYs۹9ڹl u(KH bmXJA(_GT잀ٯ; jU~P) &d2F&_py#tlRTvKgSǮ8-\PjZ.xͲF`qpĒ$ԒƬĜת߿_t j?xnTȬ)p*1p$~9_qȖ+\~!GpH #q][Ɗa'GA_E,R6 Dw(3C#SG(Xx v7F:G5r~ACEU֟#äc܍ڹTQF?H醭}mpHaV_p<ڂ\[1YjI1Jސq [],8 ^ĴGBÎܽL%XOYI've[mBf 3XqG_SYO2i&SV!`dnD̫)M snZK+>O!;u ݶ6[[BG*|e"Cִ{w ]vզvEb;<R$Z&WDpnKh8`EM+FʏN(}v/yv_܌[o<-2EdhYD22tb 2gR6vlf۴kh[wrK(v٠K4=4&q/I(WSUYnhcYwk={cm,lTlԁm>2ul3Kr*J[؞ fiX($H;AŤ?O¨`EF*@%%w1oaM#57#XLL :V Ƀx -CUˇ|.J,ZLH [Zf<]lMrJ<4NS#$;+d,k+'{ߺ^׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~ls =W]Wkſ.{ߺ^׽u~R%rojm?Os֜~̺A׽u~{ߺGjwbęC엶ɾΜQײu{{^׺~O5|_>1 :{8諯{^׺u{{^׺u{{^׺u{{^׺u{{^׺uǝ^^},~޶9}{ߺ^׽u~kC &>ˁ#m;h='^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~}zWdR3b?~ލgBg{{^׺uܟ~_ִt{^׺u{{^׺u{{^׺u{{^׺u{{^׺;t{7]oǍpwmǹkY)&F HE,|)v :)'ׯ{^׺u}(ꢡ=}-l Fۙ,,S"*ykC=MC֘TW6N}vVߴ35 ŃF#e1ӽ=TrE4e]!G -}k{^׺u{{^׺u{{^׺u{{^׺u{{^׺$t/5Y퍬`d{"٭ FTIl+ Ĵ3SȢr[tJǟ3M{ߺ^׽uw(W?aiW_:?jK׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~_><rop'auKOѯ^׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u''<rW״7Weϴ]+׺u{7v᳞U_=h뢞{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ue+eg{IwV:GKzu{{^׺uv'>Crs#?~W{^׺u{{^׺u{{^׺u{{^׺u{{^׺G[Ɠ7 nmYJy!&ܕ81TqʧghgQ+ ǥvr]lK:u{{^S.=/%?LJ]ӷ:Ξ{3r9˺sXͽѨzc1Y?C%WSWP !EHCnv.qn;m/,7‘ƥݏT=pl'7+ xI*"f? =mI܂#> 蚎Evrnp饊IQ2q>o8a4rϽ?;ɼ(.Y(G;R5~ۮv+mK-$g F<1%#Qh}=5nfgq`۴57bjʚ|mZzJX#I#I<D~{'r]ò,rKdkyye4  7wyj?8?3؟ .-z|6Szgrx|FkBE2`w`W՛#.XhU)X+Q,D}^/]kq]gYCM>xSCqMWVyEE= N( I jf^CНVߛгˏI'>7[+l^?#k;#7n%lɩabLʤF-Z屦U &uXgȳR+Uf;<.ZYꢑS$ ǿuSQGWOomzbT-7!A$k2QOwADuΒZ7_+9kHr;~?();ne}^\V+=&[ C WY#ƼЙuseη߳1x,QQgjq4/FGe! jc5}=p9u6^6f D8fr# +MbvW8#4J֝/ʇogRmdz]'ޮS f[y#[5*#F*䥎E;]HGwܶym㏜SJx$~9)^'&sLIx7:HQyIo/;QTe:V&ϲ*Q'nϨ;c8,'tw+{U^0 n ᪂kAi SmlJZVoMOCD-~t1]Z=͍ q-ˏ!\eV+:}UCA,ȱTbKfOLT윯vwf{s#qx<:xu)_ {R wtc?'[G557Dxd1&Z}LkʈQ=5{u[)?,'py.]qj RSo}Eu)${_r/s7\BPa+F6V27;g5?8;?& ^{i㘣^AI[ۢK-%-i9O#}4I柺ϽCȢasc+mٙ1ZIm8K"us_v=P$Ҍ8BU~"SH`uV;sp#Ô2{~}gXTǁ֮O歿Wo潨֩*jSPj#vgw~әK[_-@ѐcob3Ãayo_=??_HrXC׽u~{ߺ^׽u~'욾C sGU|=tU׽u~{ߺ^oHz7rݿr;hոm ,e*0 ^ܻ^pǪ%JOU6#:t2k!Mz^OFs;[Oپ3^gxo?f_xz3׿ឿ?žS7!nEMyuI6ee;OvmOHWAۃ%DvUQT(}\^x?N.l^ݛ. @ZMQde]i筤$0XFTɒ6 :@˥?v{ߺ^׽uY>DnG{e{lm͟Oz:fm<=]TMeϵ8m+Bc+uឿ?ž폩7Oxz3׿ឿ?žS7!nE^z\o}L=ts |x*H 2yjRE ]q ~u/OzO> FlιכdcwעV Co}c9mr# nO<4Q,#-C"U9o{u{{^cO'~_(h8/һ^-u'=,׺u{{^ꗿ.'{|Si{>e{{^׺<S˿$d7Mt?ڏg׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~n#6/:)[)s7}h4J^ .פ z-SRkw)t2!Mz^OC3vطޟ}f/?ռ?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz/w]2E$iHtY2̥VX4&Hɸ8者u溜Taď{˨V_'6/xg hkV2ƦJqq|.d1)PFGzo{^׺uǝ^^},~޶9}{ߺ^׽u~kC &>ˁ#m;h='^׽u~[j|?cvN+ln L|;80t BcvW1LgAqHe%dufi iqPH<:U?DEoM+*}BGqȹҪ8fC,"g__˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?|M+3'ս~ݻg5Edv&-;;qPVg;N i!̚cU'zNC֟zA׽u~{ߺ^ǥNu/#=+3G~t&{[׺u{{^ZQ7\1kl^kAwI:u{{^oĭ'\o&;7ƛR9L#:|%yWz="KK1VDfy iq:v=O[Jʪ#xw\qr.t=7K<?3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?7}k`>Z5+9SG *,ܕ]a:yj 'o#k>N7u~{{^׺u||*f)ݽ57riܑϚ)Ϊzճ{qUSMMF,4QGz.__˟bz7T"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{.}] vk(qlj2cr\fwNG1U1UE%\K/1W+Ĩ.3{W^{{^׺1?>ܵN \XZ`| 7*ZQl>.uiRHvB HVu_^'ECIISiyHE Uq3EAO @J(wK/Oz -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/OzXloi _ԙlQ6ܽBHg7vI4RL̬{ўV-!UUPUP,N߿u{{^׺Ի e+bx0+}P}%׺u{ZS? GqU뎭8[obrSa7>op'2tRÕäjGvxbA*kƣOzzČupli -lhoC+"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"gigHl@m[z'#ܻqFݘMфݛ @9c3̶vTx*D$'v}Т:ևu~{ߺ^S'.| G_Dd~{ߺ^׽u~'욾C sGU|=tU׽u~{ߺ^o(;22gչ3m, G1#rmZDe1C%JOU(MnV.Md)K=#P=.o}7>oC[ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"gr'$RF$7E.,,UeO3Bdq#~7^x?U/mHGeY}7oncb&5+#'U =,c+xPi6/|.d1)PFGzo{^׺uǝ^^},~޶9}{ߺ^׽t޿6sǭ|=tS׽u~{ߺ^_/񧨻nWwjuʽ6+Mpt{cNk,v/% SVrKR:2Fq rhK2#i -lmL={s;[Oپ3^gxo?f_xz3{{/y Q8U \GV30b-mhtC~׽u~{ߺ[(&x NOzc)C`MKsc e2+ڙLF~zAMABGfΌ1A ~s;[OپL -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"gnoSyD]gݻ+[r8F/'ԃWH iIjD*;-ȕn xmV鞽{{^׺Os {%>ޕZGme-׺u{{^׺| ˶8_tTO@o^{{^׺٧]+o=kw|mef&vޭӴ;[7sf"ٹ}vYgC,Q*"_B`C 2jlՒ=.o}7?S7!n"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{ -l~u/Oz3vطޟ}f/?׼?_˟bz者7^"guԹotRQ̓G{1Bjy~îWB1 O}D^"V6,^x,>x:8]1QSD#EQ=I&N׽u{{^׺|TuAtrcz7γ{^w7q\^]CEW2*Ԇ;gn֬lT͓Ԥ<`[lVmrRpޓF(?)cNc۶{~@ۤ&ȷF8ՕKC?oWȼo0ݩK"wrG2]g`1SO[ ot~Ouwi u aQ^%|`쏖_:YdqIܻ9,`i)*+tH1ď3W͡, wJ9C+W=(73<u_ 6nEKSYwMM5<{wE/=FT%<1:MѮ׽u~UgmefV{^xڗs[˰a鱐LoUYY4"GZe%CaݻW2#9|y,w+$y .Y8$(Tg+iώ)T^軱6-vflm}1 `qƿ@.K1,I>*u~{ߺP8~c_Qeq9Z*vO@մU)-5ee4H#`A#ߺ_'?F`h\wFRiC=U[ Q=IhvU*3,u -k[Y-ڕV0+64a me;m-uw?a$jrJddU+!dٻI .BA:[4MM TGAE Ă8(¤qFU@ {d׽u~k z~mC?wflnlqGNO'3\VFx윮*\r*Ed4-uUOWm۽O맦wY>"BSF9c,w 7ͺ(TcQOAn@5 cQ&o:__{P±'dn+ d/S6O+癔6Uظ c6&I^l'm$TtKn6M(De?Dp7w^ʮ?X'ӇѠXzu{{^׺u{7WoAh🳯u{{^׺8Υg{&/>΄u}{ߺ^׽ueaCY_{su{{^t/}d!ę[u:0{{^׺Ի e+bx0+}P}%׺u{<|gk}࿟JxО{^׺u{^{DN[yMIӏٗH:u{{^-O._(]7Ӑj>޷Sџ^׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽uq_Ka-Lz6_} u{{^׺u{{^׺wF}sfv~g?.ڎ׺u{?<|×W⿟K-x}_^׽u~{ߺZ‰O[g/ZkI׽u~{ߺWCw[>[iu~{ߺ^׽u~{ߺ^׽tWg?|CM🳯wzu{{^׺8Υg{&/>΄u}{ߺ^׽u~kC &>ˁ#m;h='^׽u~^?V}K!E(ooe/׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u,[ܾZht1O~{ߺ^Մ*x'97훏[^};=te׽u~{ߺ^׽u~{ߺ^׽u~{ߺZzcl_ofzEu{ߺ^׽u'?/l_wϴW]u~{ߺ^׽u~{ߺ^׽uZ')'N=hӢ{{^׺1J|y=OՓh쟣^{{^׺u{7WoAh🳯u{{^׺8Υg{&/>΄u}{ߺ^׽u~{ߺ^׽u~i{?#>u_;_3}U GLu~{ߺ^ПOR_^q_ϥ>t{^׺u;<{uU~8ïGzu{{^׺|-9vyo=Gt{ߺ^׽u/+;2ufdgEj~ވzo{^׺uD?DoN[y_˥}](׺u{UB?8=3}~ޑ]|C^k:K׽u~{ߺW A}ҫ_m쿥{ߺ^׽u~{ߺ_;OR!9vg ?i ~׽u~{ߺ['w?o;7]tcCV_펞׺u{{^׺u{{^׺u{{^׺u{{^׺u>?_|P=\ixuo;{skmwg2{ug1[s1+TT{;e nIscKA8Zm[|mydcR5,(';ze2y29lAyn^?%=Orߺ@k¦U`Nܑ=AziU2ku&b./һUnjjwF᭔Ud3:Pi򙺵" i`2D}׺ |C? >;^H_`)O=j1q.gtf嚪Qr+(}׺2~׺o;m:+O=N&1RzsJ$،ag1^NB*~:XY%V^긿Wb(?6n=IQ{3vݔ8*LV  EoAy-8^HJɧ{}u)a1bP:(q y*jd&IDRI{^僸0[2-Y|=r{Un=j$4vءmC+]u^:g:+qؽY0?o** S*Ueo q%^s?i$kgз{W݉E7{n?ܝUn݄z|ػg){~=]2*Z}3JONgG+/LQڔiۯhpSn\6Z}Rfi+M u4O5;T'C3[?{^꫿Xd5BRi7 m6 XI)5:lDXVZ<#K×wF[>Z?Z|~<>֟@꿾ǥq=+otm.B m#KM>s_$c9 |whűFZӮ}RAFJW+?o0w_A*a߯ԟNFoc!׺u{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^ʏN] gx_oΛ׺u{пgcƏm/ϫ{u{{^RP7L {?oH!uB~{^׺u9 ݮ}+ߗ[B{Cν{{^׺u{9l?ϵ6]'N?f] ׺u{?1LvKSdgNCzkOF}{ߺ^׽u~'욾C sGU|=tU׽u~{ߺ^ǥNu/#=+3G~t&{[׺u{{^׺u{{^Ke߸?eOj:c{^׺uǝ^^},~޶9}{ߺ^׽u~kC &>ˁ#m;h='^׽u~]! ߟlinQm˺_׽u~{ߺ^׽u~{ߺ^g_';k[+7~ξqz)׺u{{^ҿ':[#l:=׭~{ߺ^׽u(./HN{ߺ^׽udVVl-ZLFRmOCE *|ԧiI ]$+f'5\FnkJg2#9+Ѭi' KKU ?P=%/f,!y;ݑox ,:9g޳[}O^c!׿>NwdzYCw#~%׼Xurςg/CÖ|?߼ ?{YvGK'x?'z;?m_={ŏ^,!y;ݑox ,:9g޳[}O^c!׿>NwdzYCw#~%׼Xurςg/C }ҝhȣIYQsY*DE:12xUsTj#!:A^^׽u~{ߺ^׽u~{ߺ^׽u~{ߺTq?zcg_j??:Muz'ُHzu{{^?gۛ}->Qʺ2׺u{{^׺u{{^׺u{K@R16/﷏ O" W^{{^׺?6zv+ ׋~]m K:u{{^׺u{{^׺ nY{~ezfqiw^׽u~_><rop'auKOѯ^׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~}zWdR3b?~ލgBg{{^׺u{{^׺ux]\ٝ_O˪:u{{^hOO'y)?ho8^chW׽u~{ߺIng={zͣE={ߺ^׽u~} MgW<7c#~u~{{^׺Зwę]oh_ɿ?oDs۽7׽u~{ߺ[""\'-[wo>nu~{ߺ^֪c?oH!ug%׺u{J? ]U~ζ_޽{{^׺u{)Og{EOU׺u{6_Aw= $|}㸷9:7]]%rS B@J di =-T ?>Nwd_==CÖ|?߼ ?{YvGK'x?'z;?m_={ŏ^,!y;ݑox ,:9g޳[}O^c!׿>NwdzYCw#~%׼Xurςg/CÖ|?߼ ?{YvGK'x?'z;?m_={ŏ^,!y;ݑox ,:C k)iPuLs̐DѣtTrt(A>&8rX~e׼Xts1,vgCeqY:JznOUv?!CWK[C[K$t0rF̎H>Φ^׺u{OWOx/ mv?Nzag:zO;'gs Qz?F(ΌO[*8aUr_j\ 7yoy6B1SSxtVZsy*.VrzqЕi>c[ѓA &J:^y6K^ŧ`UvVŏ>jJWpʬ s^ί{Et+rG x%OK2?ǩ>n)D~VLZs M/S/-\DhEZin$io!|n"1q']}Ku~h] s;crK>Մ/%K=6C{RԤwmW Nd p=uA_>"s{wj|TƬ`Jj>訦MC%fNC`{~}d㯶Su퉋 hݏǍۻ[IQ F5g"~-=u(ȬM|>il-ۛz]sev\ml`j014LTsT53ɓrVjF ֥SNawi|N662;q\}0ZJ& !,$VAb/S_1g7wl|Xq-Zy!c3tctml"\rG6^u?~'g|W!6Sll쨣j9աZ(ۿrTb &0l uw]}oݹ2;R㨄 ZtTj^ο)؝亿'۷1(Oplխ;ssbE&(u.Cn,Ա7jXouz_ao2:cVftmAwJUNhrR׺ƒ6Ȟ~P|w^=ݛlfwVרKgl Ү_%2UEz1E;N),pI6;IeUDEw䣢v.^ʨ!hmt X>׺Cv}{M;WvZu*jLt48|h(ԃ5&׺3wko~nw&`lUe!I%49*4)OMD*=׺е#WuwNk;߶(i#u!{WS6VHQMŇ[A^?G}W=LOqvWQu <ҫ H+O:P.4귺Z{6DgsWn9"2)\RQո(c:"`^CйlտRpB3q; `jĸY7[rx*1orn*UC?OWUҹufSOHqܷ&{pEooC#,|@aGvu5(x =?o0w_{A*`߯ԟNFoc!׺u{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^ʏN] gx_oΛ׺u{пgcƏm/ϫ{u{{^RP7L {?oH!uB~{^׺u9 ݮ}+ߗ[B{Cν{{^׺u{9l?ϵ6]'N?f] ׺u{?1LvKSdgNCzkOF}{ߺ^׽u~'욾C sGU|=tU׽u~{ߺ^ǥNu/#=+3G~t&{[׺u{{^׺u{{^Ke߸?eOj:c{^׺uǝ^^},~޶9}{ߺ^׽u~kC &>ˁ#m;h='^׽u~]! ߟlinQm˺_׽u~{ߺ^׽u~{ߺ^g_';k[+7~ξqz)׺u{{^ҿ':[#l:=׭~{ߺ^׽u(./HN{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^тy` 3'UE6Q ŎIEP9jh-]W@ 3挬I:=Y}k׽u~{ߺ^׽u~{ߺ^׽u~{ߺTq?zcg_j??:Muz'ُHzu{{^?gۛ}->Qʺ2׺u{{^׺u{{^׺u{K@R16/﷏ O" W^{{^׺?6zv+ ׋~]m K:uݹֹ\e'`M&Z4ysl~_Px};pQo[G8D#PF Y}jC({csd6{j!C1R t=Vkou1,ۋ.Ybk.I?S9Ngv$wI9$6I=e]uCu]u?\@={_߿gOi7W9{P,_Fan@o<;_[[wGٻk*uK5 j2uqǦ*b.% 8år[+EҾ{{^׺|TuAtrcz7γ~ lݱ~R7`àohES4b͍bڵVrۏ>˱dJyf+JK3Wy_X䅯\ssGۍz[p6Ph-0:2J֊;uv7ʮ{Φpv>ԨP꩞r)cP< *cEw{s{j鷆$RE D@cSZϽMqCRXjguOmoFM՛ d)1E mDmv]㑮*o4/tQјlSiAܿ#S>3VO7TUܻpaCYIzϤ~#y9;ᑒq6]NUqhnM2^:Xa*>p7}Jt.ᚤ|VVwfchd$,mT^`gRTl;?5 D)2{czO{2YǓ1,n*E?q;zu_:?0mGֻx=1y\V︨!ɷvgsUAf2M1FQZZI$3FKr"I^9dt`A^|\?=+QG(e#7;yV"JdO$$X֫?]I+ndL9oigSE._@soJ`WZ3PPV+JF'd0MQQ< 3>׺G77ώC_X^zza h//L}׺5>׺cy14~6PV)(xԱT=>Ndzḿd?ߺAo&`|@N޻K\VHisnqHĂ:5(@qoxk(#U)<qۤa!G5yذGIڙo]Q3lL5DXX7U%{ZHm&f.-%A0/v1u#K Ҿ}v. -F G?8?F|noh?WOWeI4h:F:{{^׺u{{^ =domt:'i㢮{{^׺u=+2s?^ɟ?oF3z^׽u~|m|{7%:,Fz(^龽{{^׺ `?&v7hk-i{^׺u.J ؿ>->gT'_Izu{{^cO'~_(h8/һ^-u'=,׺u{{^ꗿ.'{|Si{>e{{^׺<S˿$d7Mt?ڏg׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~}zWdR3b?~ލgBg{{^׺u{{^׺ux]\ٝ_O˪:u{{^hOO'y)?ho8^chW׽u~{ߺ^ִ?ob>/#?:փu~{ߺ^"- /ϥ~]n9컥{ߺ^׽u~{ߺ^׽u~}2s岾z|'碞{{^׺u=+2s?^ɟ?oF3z^׽u~{ߺZ‰O[g/ZkI׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_Kd~{ߺ^׽u~{ߺ^׽u~{ߺ^?#Ř{ګO_]f> c{{^׺Oo6{qϧ`uu{{^׺u{{^׺u{{^RP7L {?oH!uB~{^׺u9 ݮ}+ߗ[B{Cν{e5_p_xQx}=?_# ~{{^|B%?ݿOuRgP߾??V5b^h8_w?ȟ:/3d/Nu{{^re)S{I>VO}_~zu{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^׽u~{ߺ^׽tR_|/鯚[eB%l^u dp[>Zhx69hVz-M ۅ^;K!)*&4 8p{?YI_;??/?_I/^v0_7{G׾__ǵ>{pmȦFb6QPmdTybo:<|o Q\>ίzkzTuV݃l쭭M$|RSQ<3IW^JW'[3Qt{^׺uUsS zEu{=/^׽u~_g'WoJ~#u~{ߺ^׽u~|>NJ|?G/:*7^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u})^/;K}*>[׽u~{ߺ_oq>(?N_.A4<:7VVC>FS42Mݛa6#ɞF@ղƠ/^[K'1W%"fxW#&.̀=xU=#W  })Y SHsSȑ!,: c@JZQ o7J^hXfy۟'S suUHaJέ8*\RVԷ^;{{^CL̃7; 2?2t.tv\xԓcv%Fs!Xu[{tC?-ԧ/ZǞA11 ѩ*KX%3CI U},u~|/>Q1?{%6z.3-Es;b*<r82rԙ-DH] mw&a vͨ?>3,=ms'b +m~ ڍɻi26C1ETD]Fɻ]d2%nTG?̯pIZ{ywG38Dw'3͚j䳹j$VO/[6BbT;n}o ,%)j? F~J붶Vv}64_DE 7 n[{A*qOjOA7ܱ~{ߺ^׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽uq_Ka-Lz6_} u{{^kG'.ކټ?/d7BM~{ߺ^_C3cG_taoսM{ߺ^׽uw(W?aiW_:?jK׽u~{ߺ[y?GU|Ey>o˭=g^׽u~{ߺTq?ڛO.g.u~{ߺ^Z]&P%o!}o짣>{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^׽u~{ߺ^׽uu~E?~]T'1׽u~{ߺ[By?I/U}{Cy>Z?o[EҾ{{^׺uܟ~_ִt{^׺um6?~}(qe/׺u{{^׺u{{^3~-d֛?g_8g=~{ߺ^׽uq_Ka-Lz6_} u{{^׺ևMr}G?w'Z{]N{{^׺u{{^׺u{{^׺u{{^׺u{{^׺^$߯{^׺u{{^׺u{{^׺u,[ܾZht1O~{ߺ^Մ*x'97훏[^};=te׽u~{ߺ^׽u~{ߺ^׽u~{ߺZzcl_ofzEu{ߺ^׽u'?/l_wϴW]u~/){#-ԩ<uYO[~{ߺF̬/}~*?p:N1ՙ}:uC۹D$^߶?#}t]׽u~{ߺC/)O?{I7z|c}k׽u~{ߺ^׽tj 7|cUuqW^׽u~{ߺ_G9ԿدdeGЙo{^׺u{{^׺u{{^׺u{{^׺u{{^׺L_wo9U֏mκ)׺u{{^Mo+?Dھѿikteӫ~{ߺ^ք̯˿$c}CMz#龽{{^׺s9o7ۿ.gt>t{^׺uUsS zEu{=/^׽u~_g'WoJ~#u~{ߺ^׽u~|>NJ|?G/:*7^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u})^/;K}*>[׽u~{ߺ_oq>(?N_.A4<:7V[A#>]=tZ^t{w+  詁48_ pWȹS~Nh;#\ R]p ,9Q՘DR[%nT>MH.[sRk>=D-^Ͼ>D'fwd?oY鸧C 5[%LxlRhhq8z(18AEKGN׺wևoۧ3[#bo w[e2mG ^gSM">ruR@~}׺c:H~T(V*hdz:tAA+i F(Qk[ߺ@k9ܹJe3|c5+ rOWU(5]NtA{(=^umn^RH7v[/w"9 #yA{tR>{w~|:L|իYݢyLڕi7R_X_'~ne r:)/Ku%ng+޻{8Zq{wv7aIr55WSyr2=65BD{t |\'~^&N{ ت&8"0SV $*׺G}ȭ.%U"4vw~3>h2X1mt{^y_~Ϙo!;+:7%@t8JYHOm50 #~|jkj+kkj&Jj垢y\ĒI>׺E Nm1_gS8vhk躏b˒Ȉ|&eɦ* ,Quu|Qo)7}ͷDjj{S}CQ(|zɩ5Rbu__2:3jGld}>(flw6匿L8$`uxU}crmѻK H(sV=2?BO?yn O2SaΫ`|r۽;ZltnXbS$Meb"JqW 4t,䃿qie% W!D ]3ry{MO4i:b7uwѮ nUA*qOjOA7ܱ~{ߺ^׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽uq_Ka-Lz6_} u{{^kG'.ކټ?/d7BM~{ߺ^_C3cG_taoսM{ߺ^׽uw(W?aiW_:?jK׽u~{ߺ[y?GU|Ey>o˭=g^׽u~{ߺTq?ڛO.g.u~{ߺ^Z]&P%o!}o짣>{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^׽u~{ߺ^׽uu~E?~]T'1׽u~{ߺ[By?I/U}{Cy>Z?o[EҾ{{^׺uܟ~_ִt{^׺um6?~}(qe/׺u{{^׺u{{^3~-d֛?g_8g=~{ߺ^׽uq_Ka-Lz6_} u{{^׺ևMr}G?w'Z{]N{{^׺u{{^׺u{{^׺u{{^׺u{{^׺^$߯{^׺u{{^׺u{{^׺u,[ܾZht1O~{ߺ^Մ*x'97훏[^};=te׽u~{ߺ^׽u~{ߺ^׽u~{ߺZzcl_ofzEu{ߺ^׽u'?/l_wϴW]u~/){#-ԩ<uYO[~{ߺF̬/}~*?p:N1ՙ}:uC۹D$^߶?#}t]׽u~{ߺC/)O?{I7z|c}k׽u~{ߺ^׽tj 7|cUuqW^׽u~{ߺ_G9ԿدdeGЙo{^׺u{{^׺u{{^׺u{{^׺u{{^׺L_wo9U֏mκ)׺u{{^Mo+?Dھѿikteӫ~{ߺ^ք̯˿$c}CMz#龽{{^׺s9o7ۿ.gt>t{^׺uUsS zEu{=/^׽u~_g'WoJ~#u~{ߺ^׽u~|>NJ|?G/:*7^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u})^/;K}*>[׽u~{ߺ_oq>(?N_.A4<:7V[$= Fn05 oaa6%+AmğN.EoM}Ч,v?K,8!q߽מmJlSr+/wcK)H|D;ۼ;+3w ,vhgR8i_SzcϾ}9U9C=h X-թk!7kps 4E؏_Ro]5ǯ:XQ}o^t^`s*݋*T*_'p?!4~׺!+/wono޸aXi0Ճ sVRaid4+)){uJ6{WFbtk0?&뜇ش"<{a{O҇;%65{Gj7-&ocO_O5ʮu&xe{ǿu{y;CW!C|n.-mlH6w]`08l\?af[[mI* 'B=׺QI Xo!gFU>"fRr] Ԭ86I𻨳'>3vx}k[+iwV'qmn.b zvSP*TijV [E;Ql˝ߋS%#B-_ p.|uiw6/݉y|%ػi̞SUST oijj祤JI$%8B$^ԟʯw?aAg7xs#f{7Yݲ jŻ7 yDK4USC3F^5>[ NFvvSH)F뎓豘H#fJ&j|lQTJ{'֬]u;;^D׾k۳  Ի+7 ~ 5"jyw:567f[t HtT>RuCKqE6 R27yTH ׺3ҝl꾏7Gaf1;5Cp5FsvbvWf(:VoT=5,+4 #qP^F?˞['ylOc܏mvgalmǰ: +VkVٶ<x΅mЏ&iq\N&qz"2]߻=qnf)L4[fg30io˜5E2dI矻7jlM;sQF 25e<$f}>ZCrwk2U_ OqS~{ nUA*qOjOA7ܱ~{ߺ^׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽uq_Ka-Lz6_} u{{^kG'.ކټ?/d7BM~{ߺ^_C3cG_taoսM{ߺ^׽uw(W?aiW_:?jK׽u~{ߺ[y?GU|Ey>o˭=g^׽u~{ߺTq?ڛO.g.u~{ߺ^Z]&P%o!}o짣>{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^׽u~{ߺ^׽uu~E?~]T'1׽u~{ߺ[By?I/U}{Cy>Z?o[EҾ{{^׺uܟ~_ִt{^׺um6?~}(qe/׺u{{^׺u{{^3~-d֛?g_8g=~{ߺ^׽uq_Ka-Lz6_} u{{^׺_t}(3{SWW )1nͱKWU,TYcht0; yDm𞘞3"i{k+]k2ޚ_׻ y%Y].VWR >CS%`W?ݙ&Wj_Qִ[vg^}G^އnMٟn{{Kz}7f ڗu-z?ݙ&Wj_Q״[vg^}G^އnMٟn{{Kz}7f ڗu-z?ݙ&Wj_Q״[vg^}G^އnMٟn{{Kz}7f ڗu-z?ݙ&Wj_Q״-vv/OڻlIXO HK:diuFZ ='=u~{ߺ^׽uIѿ^׽u~{ߺ^׽u~{ߺ^׽u~Q9_Y}(5c!Pf=!׺u{ TOsno7طvG[{*˯{^׺u{{^׺u{{^׺u.J ؿ>->gT'_Izu{{^cO'~_(h8/һ^-u'=,׺BvVťmڵUo@fzJhi&Yh %J:RQ6"%}ޯm$7 !x\mO//~lG_׿˗ΌGtLYW\Fb\|REE5N U=4D#Tժ0tɽwi ÚomE: %j $y^8PGJ9ji0÷@IdgTaj(f$Lo,׺ nY{~ezfqiw^׽u~_><rop'auKOѯ^׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~}zWdR3b?~ލgBg{{^׺u{{^׺u{{^׺u{{^׺u{{^3ݿlUWcZ<:{:觯{^׺u{7#j'FLѤٯ?їN׽u~{ߺZ2.+]mKtY7{wu{{^qCdK?n_mMҎ{{^׺W#3#=L~'>ε{ߺ^׽u})^/;K}*>[׽u~{ߺ^׽uF[}]]rvOvoڙf=;qUN?Qbi(VA,ӳٌ`YM ,z-3@M֯j5/-z?ݙ&Wj_Q״[vg^}G^އnMٟn{{Kz}7f ڗu-z?ݙ&Wj_Q״[vg^}G^އnMٟn{{Kz}7f ڗu-z?ݙ&Wj_Q״[vg^}G^އnMٟn{{Kz}7f ڗu-z?ݙ&Wj_Q״9 jf'hxdU)J "ֺ{{^׺ue+eg{IwV:GKzu{{^S.=/%?LJ]ӷ~O!ue2O>Oe!mB ;Qtv~l8iߝJ(O'+{Ǻ GhIau夨;E3)(8e=4FC&d6}_K݈&8SNWu*5^׽u~jľal= +$*}uJ/[we [K;zi`cVNsu׽u~{ߺEC̞-л.4/E%U,{wLI;JrB$1QRG5T`F|~W|쟘 {7ndc]PS\V_ ji`תiV/,$I#hVyجjx XDžh8 yu%p]Yˤ$q`dv%#Jvmڍ& ]VfXc@u^׽u~{ߺUW?]r]MЛ>"3nOQilk8YVird{O'vSa|-];}dTvܙ +ORO4p1coލٯm/_0:Ѥw MSA=ogwR$ [|m~ÐT]5f_8L&# M7?+i35J'̼ 7#IB[,<<oi^{9㜠"yck 5Gki? ׸q1,i֠~Uzu nUA*qOjOA7ܱ~{ߺ^׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽uq_Ka-Lz6_} u{{^kG'.ކټ?/d7BM~{ߺ^_C3cG_taoսM{ߺ^׽uw(W?aiW_:?jK׽u~{ߺ[y?GU|Ey>o˭=g^׽u~{ߺTq?ڛO.g.u~{ߺ^Z]&P%o!}o짣>{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^׽u~{ߺ^׽uu~E?~]T'1׽u~{ߺ[By?I/U}{Cy>Z?o[EҾ{{^׺uܟ~_ִt{^׺um6?~}(qe/׺u{{^׺u{{^3~-d֛?g_8g=~{ߺ^׽uq_Ka-Lz6_} u{{^׺u{{^׺u{{^׺u{{^׺u{{^2틵wpn5Xmom5DH8}ϵ(ꂦjJj)nѕ2$Mxu.Hisc5Bfu~{ߺ^׽uIѿ^׽u~{ߺ^׽u~{ߺ^׽u~Q9_Y}(5c!Pf=!׺u{ TOsno7طvG[{*˯{^׺u{{^׺u{{^׺u.J ؿ>->gT'_Izu{{^cO'~_(h8/һ^-u'=,׺u{{^׺u{{^?/OROeo?z٧E{ߺ^׽t9|b˩{}'>/?F{ߺ^׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽uq_Ka-Lz6_} u{{^׺u{{^׺u{{^׺u{{^׺u{7v᳞U_=h뢞{{^׺u ~&Gݫ_1Ff`F_:^׽u~hKNLv7ٴ?/dڟ9ޛ׺u{?.[{c{-}WC7J:u{{^U_P?_?1_g🷤W_:׳Β~{ߺ^~{lli.?g[h/o^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺZ??!va8:uPGO&v%J#>SE%b@ - (G=!0#P}&׺u{J? ]U~ζ_޽{{^׺|TuAtrczS55i{7:64pR,YE>ḍBy)`"f>و`M(+ ä/L 5:Y4j~FOkv x i'KS1/o2ë+14;jw;җ+溧;[Rp}ĸ#Pth v'Y͇mfVmXZBxUcRT:υ:6EO\L 5+~=n?E|鮼NL[|ػwT+G,X (1f2R+6f'E^yikL#׺bX̾o%qM~S+YMPӥY[W$4ЭgeQ}uASSܛG$CQ6ˮMԻ''*\6rk8j'Q% }׺v_go]ٝkw_av۸2Mf3kn-2<pq"Fm=6YR *ĀHG2 W/6o?揮;sjcvMX EG;tU0ٛU2o3&b5UT1龶fۏAlf{W1Y*9T5tgyY#vFC{?~ߔ;<5~G{em|2IM̔;"Fc:4k&Ei.{?w?̓{bpXLFC~3N!}]W Fspj}- =|l =.yai䚢tח}^:m՘d+ՑFz ~\vϐ;WOGUAd 6E~rK ͇cBij**`D.wnbP1M)>ۋy-D?#־!ilIܝ 7da2t8::i:># %-Azhκc(j@mG-ϞZӭMTQNep"s2ɺo$V UH(1!Te(A '_\OVgS;|Cf+:(*omZ\cdnh|,`$cc+i$|}毿m{omp`L@hOmxB,Umyd2~^`HV>l/;S\S&FcvR[)=7e&)j1Ӛj|R,<C'bv˘vK 4>`#xS ޾ [_^Žj\M;nv۬ UKU FfņCguS^5mm6#WMAAG 5uKIKOz8`$3P.Ouk۽_&ASM=i|QuEOavF8`1h1d2HRCL ^C_!>J0otbmݛh+%U${w`I q-TDHM5L9{݂~ `EK? 6j'X RI c=Oug-?-ߖSsw^y쭻E׭Y豵MI&g2;4΃g崞]]xV6isȤUqrIm%cqo0 w4Pf!V_*7ˎFHVc82sq̑3Ag/3e|^QD{Y{Irݶ7W[A9G4]Dg'^{wc)CAK@fU+(NѨ)厤{{?8?F|n~ʮ?\eړihu,u={ߺ^׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~}zWdR3b?~ލgBg{{^׺Q˰oKtY/P}{ߺ^׽u>Lo-[eo~t^׽u~j] A}}Z|>ΨOھ~{ߺ^?OQ>^p_ϥv[hOhzY׽u~{ߺ^/="\O'-_ˤ?}iˤ{ߺ^׽tx/I?{joo[{)ϯ{^׺u{7WoAh🳯u{{^׺8Υg{&/>΄u}{ߺ^׽u~{ߺ^׽u~i{?#>u_;_3}U GLu~{ߺ^ПOR_^q_ϥ>t{^׺u{hD'p?}_zGw?u$׺u{D?[y_Az -_J-swKu{{^׺u{{^׺ dm3ߟe}>!O;E={ߺ^׽u~}zWdR3b?~ލgBg{{^׺u{{^׺u{{^׺u{{^׺u{{^׺(_?>T /=/muo_^׽u~{ߺ_Kd~{ߺ^׽u~{ߺ^׽u~{ߺ^?#Ř{ګO_]f> c{{^׺Oo6{qϧ`uu{{^׺u{{^׺u{{^RP7L {?oH!uB~{^׺u72|Q-ƣ:j*]!K Kh8/һ^-u7=,׺u{{^׺u{{^?/OROeo?z٧E{ߺ^׽t9|b˩{}'>/?F{ߺ^׽u~{ߺ@o&{kx>_?g_;Ogu~{ߺ^׽uq_Ka-Lz6_} u{{^׺u{{^׺u{{^׺u{{^׺u{7v᳞U_=h뢞{{^׺u ~&Gݫ_1Ff`F_:^׽u~hKNLv7ٴ?/dڟ9ޛ׺u{?.[{c{-}WC7J:u{{^U_P?_?1_g🷤W_:׳Β~{ߺ^~{lli.?g[h/o^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺZ„qzg?"ֽt{^׺ue+eg{IwV:GKzu{{^S.=/%?LJ]ӷ>|_nv _{~mdX9I+d<$;2FElKݮF[Xj%7(*J0f@&7m4`e/>3Io)@t1J-d;Fɟ.> mn?[(jCܘ!U>3ٻRᥬcc&xnh{Wtr'U%Qyfͥe'J2IP9cb+)yn?-s4 m19 aqFVSB(A֟?$o\7c[ZIM/gv;!{ J9+sim0fiqgI>6ci~{c vH' 28G[=TC}ד7}XC*OEi|޸=ߝӰԱ>߻Z:R 40B4&%A"^*;$A÷Bh?M՛˺/.=۝՟=62zv.bJ K\[;TLu(KUjכ V*Yç!omK~*V1|-Uv,oNle)Y3Y~W״xT2z̸mEvBZ96řȅQv;_ /=X_|n!Y;3KRi?(if! &ҁdVzZ\'S==ʏVQ1o ݢ, $yTUч*FZ#'GBmĭXGZ|M50duM U͎?"@˺jw^*Ah#BdIOe?zÛnitXbR^[1[-ŃA8| #xκ%Pzn;r4cZTCV#?Oy{{]84j` ڬG@6ؘj>{N¥>Z|4Ue=&.QcSw=tRRަOe)Ǩdq.v?T,n~:gWU԰t^ݴݷYvToRYkw}N VW}~}mx~* = ŹlGGV_m'Θ'nl >jp1M~dba=<;W%NO0Yes#(emmhjz$N I;,+ԡr%EI%tcq7x~mEQ_h_ڰjZ#OW6#;p:fhc B)}~2%)MbRH%F:2Hc~h丷&``W`V)UjyӇq֥l\}ѝ1uN"ߘUFڣOxmۚ6JWq{aohӺ Lu"t'v&ܹSwX<ޅWDڿ<>drb6O;Gxv9#_4:dH6s\ 1+3etQ;?nv7tujRbw`oȬֲ)sl̵5~H./Aeۏܗ۵BZF09{+HOz|ό鬗Pү>zbzYdTD-4Eec>}8%Sr֠GkO⍾4pW&zC^?%CvۇWocEfo{-q] ^VTώKPxRIy QΉ1<7Kz%[EԚc,5Q;(Zj#Jj19Gȼn٭ޥm(<2XԀ $ g9/Dc=%^ a۽Ӟyqu*HuݗjXO^xvU}=ڽ^gGKoF6#\ƀ-DO#2t~fV-tfXi$gC{H݀Y'A_$3{`wwn틕o>8>~\;<K,TU"}\4$ _$%U %Žgw7yDžz ԧkz(O`)>3䦧މ޹MSr;6i)s6'~Gi|-M[!\{zg=ȶI6=X Ʋ @tܥ}N-Õ}&<ە?uo|]"ڟ+Fm()) JXijxj颦njXFY(WV 3';r{ >zAݏIm}usvvt5\`7}Q#S$-3'˻%v@g$rOG$8} t[e#5?tz>6'_$2Di6dQfiq{D!jrDr5t KdF6ҥw,E:\0k1_{~ +v9}O4bEOG[4}=cǏjܶFBI=VcNB{ﯼz^ha)inKN!qpBCSϒ="yjKcB/qv (ww˜;!r{[6zUbzi/L#(%8.aөZv}6h?}~>ǥ^(n4!0H|\jzZ+G"I DQʌ5GA"nYj]_"Oj0ExbyB# (Br/я])?Ϭ\?52{я]{>A /ͳS+7}/׾\?52{я]{>A /ͳS+7}/׾\?52{я]{>A /ͳS+7}/׾\?52{я]{>A /ͳS+7}/׾{vVꍩ0.OlnmnyVP`ؼ#A,UHR$31E0Ij:ܳ.)^ڮ~{ߺ^׽uq_Ka-Lz6_} u{{^׺>F|]UMX k3=\6/mm{Kfr_o!Ey#(in6]Sp3𡼨Vw5&wS-5:ÏQM-U|mEXl%7^oy.m_.G^ѩ߾Toy.m_.G^ѩ߾Toy.m_.G^ѩ߾Toy.m_.G^ѩ߾Toy.m_.G^ѩ߾Toy.m_.G^ѩ߾Toy.m_.GEoG/[m[(hGE:˃18쌑"YLbDGYQ5#Ipκ@ꚽ?^׽u~{ߺ_Kd~{ߺ^׽u~{ߺ^׽u~{ߺ^?#Ř{ګO_]f> c{{^׺Oo6{qϧ`uu{{^׺u{{^׺u{{^RP7L {?oH!uB~{^׺u_*{#0lj 0oYM\f n[WJQFXŕXd)5tWC!xje.}Q׿!xje}_[lyzwv#x ٶ5lB۾tB +/ 2I$hU z\hlOs$sC4i,3D$R"HBQt i:U?~{ߺUcu/]qEkceS+7/J +>.m۸IK]j)i'鈒V%~+vj3,!xjeя]3G^ѩ߾TGo6.)zFλ)n,^-9]1<rop'auKOѯ^׽u~{ߺ^{ɫ7 ?uWOE]{ߺ^׽u~}zWdR3b?~ލgBg{{^׺u{{^׺u{{^׺u{{^׺u{{^3ݿlUWcZ<:{:觯{^׺u{7#j'FLѤٯ?їN׽u~{ߺZ2.+]mKtY7{wu{{^qCdK?n_mMҎ{{^׺W#3#=L~'>ε{ߺ^׽u})^/;K}*>[׽u~{ߺ^׽uT;T|5ܧV;*j١EvͦR^+Ѹeg,}=,5+-8"AzbY3u[_\?52{{.?ϯC{sl F?u?76L`c^?ϯC{sl F?u?76L`c^?ϯC{sl F?u?76L`c^?ϯC{sl F?u?76L`c^?ϯC{sl F?u?76L`c^?ϯC{sl F?u?76L`c^?ϯC{sl F?u?1;/jEٝIEövۿ{m`$5ZǖJGqDcb]+y$25OE/۝S{^׺ue+eg{IwV:GKzu{{^S.=/%?LJ]ӷ:ΞWqK-&>hf;/vV)?xj%RJj萕uV`coqou6cւIR">8'&7B@ qlE3[·DW%+2rP:yOު#1h1'-4Q5lY*SF7P7 wk,i'u% ,RV&uCKYZ^SmnrB\P<2#s"bGFopn0Lb#",n;#HƒQ ;"\v^~xm0Pb, }]Fj?))U#Yέ@/>lG7Nttqbo1gy}zz y3*{nVҷ05+P vwD,v߷02x 7ۀxքw?]M%v3`j&I#ݥjPJ+lԿu!џ3,AŞ'[8oZ(K~|HPjfS0դ [S)08Ksu=!yEhx n_Jڃ%4W [nE<~tFޚXj&iTK"yc~AmI33Ё)xA eSHN޲J/g)ke/BL4/#k^ԖfQH0n]% .p,/SMYm4O ]%~igJ3B?sAcrUdK*Q!$Jx- $ YPy,Py}O~>ZZZn[3Mjk0oݦ"?Ѷ#rũa҂F6sg܃ V7K!]+!kYCKxMFu p>ds'-^YJ -#_j 2?Q;75ERV2W{uSǜ_P)*XEޣg?MekªGrNMEvuBly- u87&[}OKP'xmj*W./sc,AxS#5j-Rd6`vg3ʣwS@0Zz)Jw qіjnUi'AVFs#Vzn=9Wtiq8iT󍣵Jy>-@Xu&ŷ)$K~a աVCsf=(bRG*tA|?+ery*Z*pR'f`Fh^:ΕwWQbQ${W7vҧI6=Vf$vn}ҕK_ogҬ9lB@ ~EG.k-nm% ! ut]c7ZzH;7c7X hk3q#Gϋ3 _Ù>m;ݹ k;zU< *#`-t-o)0xZ9vw7SU:Dn^ܑdA)BeIQA1z h,5m#5{ b}VkG5 ˧ <|[Cc"X>A,R>!P>hf ۟mE_mnu?u}nfǒ_*mvwnQGm R\[(U%gp4_筶ә|{+GKZN8;Z)Y 7Q3o[rDZiOP:A(>}Lˁ#m;h='^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~}zWdR3b?~ލgBg{{^׺uP~%ࢭ1weai[4ڞ=XKϵ$#'Z{]>{{^׺u{{^׺u{{^׺u{{^׺u{{^׺^$߯{^׺u{{^׺u{{^׺u,[ܾZht1O~{ߺ^Մ*x'97훏[^};=te׽u~{ߺ^׽u~{ߺ^׽u~{ߺZzcl_ofzEu{ߺ^׽u~{ߺ^׽uymg9Rՙ&=F 3`heWIZFbd~&Jzgktqӝ{ߺ_>|oܵdPDSAtmhԨQaS>8<7R%Erhi{su~{ߺ^׽u~{ߺC/)O?{I7z|c}k׽u~{ߺ^׽tj 7|cUuqW^׽u~{ߺ_G9ԿدdeGЙo{^׺u{{^׺u{{^׺u{{^׺u{{^׺L_wo9U֏mκ)׺u{{^Mo+?Dھѿikteӫ~{ߺ^ք̯˿$c}CMz#龽{{^׺s9o7ۿ.gt>t{^׺uUsS zEu{=/^׽u~_g'WoJ~#u~{ߺ^׽u~|[f2Y$2zkr=DV-~ধ#h>3߻^{{^׺u{{^׺u{{^׺u{{^׺u{{^???R6_wzUk}{^׺u>?_|P=\ixuo;{s׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^ _g~p4}#}a^׽u~{ߺ^׽tj 7|cUuqW^׽u~{ߺ_G9ԿدdeGЙo{^׺u?춾Tr?k=K}/nt^׽u~n0;v4~nF~}[ߴ?׽u~{ߺZzcl_ofzEu{ߺ^׽u'?/l_wϴW]u~{ߺ^׽uK{ȗa=}=Zq2^׽u~?ee^ڛ&:rG^z3׺u{{^ =domt:'i㢮{{^׺u=+2s?^ɟ?oF3z^׽u~{ߺ^׽u~{ߺZ^<.Ϯ]W/_sB{Q{ߺ^׽u''<rW״7Weϴ]+׺u{{^ZQ7\1kl^kAwI:u{{^׺u{{^׺u{{^׺u{{^׺u{{^ҿ':[#l:=׭~{ߺ^׽u(./HN{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽uIѿ^׽u~{ߺ^׽u~{ߺ^׽u~Q9_Y}(5c!Pf=!׺u{ TOsno7طvG[{*˯{^׺u{{^׺u{{^׺u.J ؿ>->gT'_Izu{{^׺u{{^}d!r^gվތ~Ώs{^K,{^ҏtW'Y:u{{^׺u{ǟN]M$=Y>1~5׺u{{^׺~O5|_>1 :{8諯{^׺u{J̜_F{ylWgѲ#L^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~&wy qG_6g]~{ߺ^׽u&[ds7?_dhz45:2u{{^B_Wewew뱾͡~΋&n^׽u~n9rmKiQ׽u~{ߺZ„qzg?"ֽt{^׺ue+eg{IwV:GKzu{{^׺uv'>Crs#?~W{^׺u{{^׺u{{^׺u{{^׺u{{^׺Os {%>ޕZGme-׺u{OWOx/ mv?Nzag:zu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺73ÃayW__=??_HrXC׽u~{ߺ^׽u~'욾C sGU|=tU׽u~{ߺ^ǥNu/#=+3G~t&{[׺u{-zfdgEhoE ۝7׽u~{ߺ[#! $ݍۯ!х_V7O~{ߺ^֥o)^م~ޑ]|C/^׽u~ls =W]Wkſ.{ߺ^׽u~R%rojm?Os֜~̺A׽u~{ߺGjwbęC엶ɾΜQײu{{^׺~O5|_>1 :{8諯{^׺u{J̜_F{ylWgѲ#L^׽u~{ߺ^׽u~{ߺ^֗˿p3U3=iuPt^׽u~m ;'U YklsJu{{^׺ևMr}G?w'Z{]N{{^׺u{{^׺u{{^׺u{{^׺u{{^׺8Υg{&/>΄u}{ߺ^׽u~kC &>ˁ#m;h='^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~}/}to׽u~{ߺ^׽u~{ߺ^׽u~{ߺTq?zcg_j??:Muz'ُHzu{{^?gۛ}->Qʺ2׺u{{^׺u{{^׺u{K@R16/﷏ O" W^{{^׺u{{^׺ki|bgCo8_׺|e1Wx_}=nuN{{^׺u{{^re)S{I>VO}_~zu{{^׺uߓM_!9wO}~ξv:*׺u{{^ҿ':[#l:=׭~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺIng={zͣE={ߺ^׽u~} MgW<7c#~u~{{^׺Зwę]oh_ɿ?oDs۽7׽u~{ߺ[""\'-[wo>nu~{ߺ^֪c?oH!ug%׺u{J? ]U~ζ_޽{{^׺u{)Og{EOU׺u{{^׺u{{^׺u{{^׺u{{^׺ue+eg{IwV:GKzu{{^S.=/%?LJ]ӷ:Ξ{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u nUA*qOjOA7ܱ~{ߺ^׽u~{ߺA.Grt7vD UdqtS $;MS: Ğ<{8?1[?g_9g=u~{ߺ^׽u}l}n'#tK[G1T/:exX~Lb~}@^׽u~{ߺZ0uv/Jl,u. އ1l.A]xOٴ9~΋&Ѿމzo{^׺uRI;Fʪ 윮.IǦQ\ QEϲ˯!хC{{^׺EV"zC" WKMEOTTf;zxC~}Z|>Ψ'ھ~{ߺ^_**)SC h|+U>C>v.]u~{ߺ^׽uMZO/6vFXQQɌݘĩapC#!IT}H_˦.gٗE{ߺ^׽t~! ZM[?o|o#3J* .* 3DgNÙG[*˯{^׺u{kf:1YyNv:uVWWl5%,9y*%TQb=0Q[?g_:g=u~{ߺ^jUUU$${Vb]c8L M=S:=v?o*uuWVxYH G#+?>޺^׽u~{ߺ^׽u~{ߺZi= .GLd &=LUTR͞슌13) Z +S_H.jzO׽u~{ߺ[HyɬҮ:x>[nhKZ~}am~޶-]{ߺ^׽u~ka !&JEI3ߋU=6C1OxIZV`i{[g^]~d}^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~}c6]e,Rjj]d<̅X~LclЅo{^׺u{jDtOI)j*jXU[NGO,hIbI,.G>/$?:c~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺNVC;a14s9 ,V6/5^C#S%,(--ELʊ$zួ}+}o׽u~{ߺ^׽u~{ߺ^׽u~{ߺTp_Z&lr8fvNێWdFX2jrYQPUZ{Zx1^׽u~X򜡬`֎j Q񚦦b8c[6 7طvG[*˯{^׺u{{^׺u{{^׺u8UȞȽ/RV7RmUeRg"7qLƲќ_ٯNu~|>ocf|y>FMrE:򝉸XV064r)fdGErhisu~{ߺ^׽u~{ߺF#&!Ukb3V;㩢i(}঩ҥ%4o4DְI #oh} }k׽u~{ߺ^׽t|w'Cwn@YlQvNJ9P@I#3Iǻ!UusW^׽u~{ߺ_Gަzy*ih8޿٘ILԵ{sOUM2X'd͖'Ѱ>΄u}{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^裩ŏY]Qî85TAVH\قܐ=qLJ_69ii"Y! xh,R WXHe E:{{^׺uY L,Y\7ǎdRRd1m+)fBYiad`y{'21F|F:^׽u~h{Ѱ?%)24RZ1%7A*yQ RW]IĎ}k͉OD5׽u~{ߺ["*_3S 9.մHR1[GZ $n&e_]/=G^׽u~j d")e\UvZҧ6C0u3L}m(au絝%׺u{|'ӔX_zlc{`񔒵&պ r=?g[`/o^׽u~{ߺ^n>v;#M-%m'{v5.#w|.`JA ?I{U׺u{{^׺u{{^׺u{{^׺u{{^׺u뎮'ye㥙=5KU==vS{*qԲf:KπJ~#u~{ߺ^\{ӧ_K6 o}=0ճѿu={ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~o0w_UtLq/߯ԟNFoc!׺u{{^׺u{y\"_f&k6nF[;;Ws>Bieqd+q꺶i%N#5i].+O>cT4??%oM{sOoQ׿7ýoQ׿7ýoQS[{?:l뽡.f(&ٗCI[QT"6:(bTQ4AV,PR}^;m'S}*׺u{{^?O_3vݑut)13 կ;F~}lZu_l՛j6~PmcXaVKQQ.$HZIfw%QzVQAt׽u~{ߺD^t#{m˶*X벛G)]<9ltfpY)"J'7'Ib(Gbj7$bACǪJYZJTሊZ8vB*i,YyO_է7ҷ7W?Fw}Zz:W?Fw}Zz:q}>@M;ޝ;üMdiɨU\-|iՏ~7 zҷa߈Rug[e{TWKۻ.;i>V JZzZxत ,ңF7JEt^׽u~{ߺAluf޹Sh'|ag;DE5]Lv'Bbxi<Z'ϳU!$Iq?0TTY39OLCUE9]b-^5\/פf"?7ýoQ׿7ýoQ׿7ýoQ/o偰IyȝYSagC؜T9 .MUYPSj'%H⦍TNPrZg:{{^׺u{{^]Ȓ9nu?u*nxw9UTxi%-2ZJKGuEJ5CNW?Fw>=TV{+?n;߾=^V{+?n;߾=^V߆ӽ?;nveVS6_LfI6|;+J*6:1*cVy&D34:v,PSm1[{IҮ{{^׺u{{^׺u{Y٘ dv>ñ#'Q=V=wܭj uA*&v)#MKgTQ/'䈖AutC$~C+HSiΑHbT;<?_j=IGX7ýoQ׿7ýoQ_|u>+ov ZZm*w$okz7 zo2)>5WQŃKQo׹r 6~i'^)D hb%XEFzVtun{{^׺u=m3]=ٰE`Nݥ2wZ*)֦(jf7BBlPnѶꎂEzG; 2ձm< &aw-QO[oXg_ίǵZ-޽CC.7x0Ɩ^n+?n;VG^~~JOC׾G^~~JOC׾G^~~JOC׾G^~~JOC׾G^~~JOC׾G^~~JOC׾GV!.}tt5T{iXڜn?*1z?J,jS%2EH*%>&2 *(;3庸iQ׽u~{ߺ^׽u~k7]ힶݙw7v3)ek$3+6_BvqQ44̰LxdwZWKϤm :)_Swa=S[u~~={[u~~={[tp> #FvN3hb r F6veVqU M4y ..WIƽl#Ju{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ ݽ;o`az5uݱwn2gϟ}[9r8`#3D/GYUHDV%WUtKpͩM+%~~JէJޣ??%oM{էJޣ??%oM{էJޣ=s o_ݏm덑ʌ.䬤*Eݙ>:,gϏBǮ9{ mCW5ldTUUTXJߺ^׽u~U X[䘽ܣ;obt5 ՃqpQ$5UR l;"HQ!'Ŏ+2$uO?_SwaZzV{+?n;߾=^V{+?n;߾=^V-n|wUّo}YLZ,wcgQjO.h-;A^8e͏]s:`6NÆ#vJʙ/-nO)_Q-U]C%ETJ䳓 1f,OKB#B׽u~{ߺEC/a] xhd Sm4UhW+`]D))r9&ԽRHċfO,dxnRNAY؜M0?5f2*OϬO0zK:@ѿ7V}+z@ѿ7V}+z}CYL>zL{̋YUCANOKMCQqpŰʣTBx.>izҷ`/_ z/VqsհfwQvn -I\M$HYZVҘG}9׽u~{ߺ^׽uK?ak7Wv^ Xe_Q;ޢ(2է'3HT8pc[+y`u 7U@ѿ7OC_Jޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣ??%oM{էJޣt?oVS&O:^"UP25SPm\s" }Zz:O¾G bV<᭦51GNGqqLH,K4#Vxt8kAǣ׺u{OWOx/ mv?Nzag:zu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺73ÃayW__=??_HrXC׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽utMîL?loÝgO^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺFp7w*]??\~'ӇѠXzu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺uyN B}QH"7)%(#QO!GΠ؃,rUӟCͭ_{އj_QԺꜥe6;ٽ{V̔t4;nUUHt55>JIF6UU,O{Az^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ_oq>(?N_.A4<:7VF9t~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽togw~U3댿~{R8厰{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{Or_;2_:ѽS7mPUbf6YQU(Q\nsxW- C:SIUZ[|#='B_oK]ݱ>ؽ9;}nܙ\$0QN(iszc$I'VКt-U@CW?FwZz:J'ӻOg{olh1Y\^%Pe38Klcjk^N24$yJFKv nȥ\D'ؽzNGwFw>^YjU[?pf`S9:WH啚DMtEBWQ7J:u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^S.=/%?LJ]ӷ:Ξ{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u nUA*qOjOA7ܱ~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^O}˲>u6>-puzBwSb0XoL\Zx(TL󮢫KQV`Xi{{~XG]<8MEU%Y<_i!XBSW,b-?HQLոyӨ((t4Xm%=;IOAACI tTTp=-%-ch=&J-+@O{0JWjo x{˳-/ßs`׈gM0hye!|% X>/kci`Xe vPiH'iEfr4> %ػ܁gPBв%=sX,pHU,>P@#ĉ?0gX]ѿ ZW{Jݯu`:>л+o?>__ hχt}awFWAr}{vߑo׿?B߿փ~#6>_+A:~G8mG^ |? 7~ZW{up֎ Gtoe~ '׿k{3Ώ?.h9_O?mZ:0gX]ѿr7ܟ^ݯu`:>л+o?>__ hχt}awFWAr}{vߑo׿?B߿փ~#6>_+A:~G8mG^ |? 7~ZW{up֎ Gtoe~ '׿k{3Ώ?.h9_O?mZ:0gX]ѿr7ܟ^ݯu`:>л+o?>__ hχt}awFWAr}{vߑo׿?B߿փ~#6>_+A:~G8mG^ |? 7~ZW{up֎ Gtoe~ '׿k{3Ώ?.h9_O?mZ:0gX]ѿr7ܟ^ݯu`:>л+o?>__ hχt}awFWAr}{vߑo׿?B߿փ~#6>_+A:~G8mGVC?!vGhE]WOI=w]n=DǐZ̄3E] {9rlql+ARiRI$=co o?zpCDEQEzݳ؇{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺ {z[O6n܆6$? @+D1XlWUf57K) rqar8e"m2iSmGI}MNUr&i3e-1:9k^֥sO &m~IU;{cnSWmAks.Vt1&_tMMy@|5vLsvNS: _KY]DJPS4RG{I4#t(4G]9x24;eJ;wR7Yiҩ)icuJ\,O D䇠zq驤ң<:/ү;)mݑ!WttMK]z6|u$A9 dyEԩJK:OoO$e7ۇvn͖-7AieeWI$8^Yu5geS =hOcp?u ;r)' |XɬnttY(4q>ԋI<9OC/ |yԭه#Iz |yԭه}#Iz |yԭه}#Iz |yԭه}#Iz |yԭه}#Iz |yԭه}#Iz |yԭه}#Iz |yԭه}#Iz |yԭه}#Iz |yԭه}#Iz|Jܥ<᭦`3=AM]iy˜%EUCIk!5΄h(Ղ:Fƙ[?׽u~ogƒo鳛ş͌C5$YnBUM$q4=MCF%X(P_^UUAfQ׷t/NF$zr8{}ӽ{ߺ^ ?* syVụnb-1 ~/ŠZ15MB<Ύ S]rJ\zG?{3B^mC~}^zs?(߾u_C׿!y 6zo{G?{3B^^={ǞУkׯ~6!׾}^zs?(߾u_C׿!y 6zo{G?{3B^^={ǞУkׯ~6!׾}^zs?(߾u_C׿!y 6zo{G?{3B^^={ǞУkׯ~6!׾}Y"t17ώ" drmIH#YnB@ǍCF:/~$|蟚[):1-^-ױU%6+ymVj*}-vF\cu6B**y"xEUz7^{ߺ^Kq?2UalͱXKcͱ`;5YOy-@M 5\0JtxDvV針=OG?{3B^mC}RAfQ׿}TB=9xٟmoF:/N\/TܻS;PSؕPd嚚xeYberѣm-ǥR{V׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺g-AֿUr'7b٨` ,U\qVc:Kus7[7 qzmw9ͼ⪬d:\Nch颎cF#ViYuh =joxٶ'?=Wb{}6m ~x1z M6ه}D׼^_kB~߾o ^S/͵?aaQ7u/A׿᩿fП0福mO{M]{ujoxٶ'?=&.:57l_f^b{}6m ~x1zs/93g _?fml*\'V9UiH֝%EB4m5Kpdgύ3)%ujdY}#Au:VEdvNjAxق ePut¶a zYhԲOONƐF}6m mDAo'oF]_C;!mmoYLUFon;xy:;|(}u޸tctkywj[[UMlv㥦I_#)WH24*X#T$S/͵?aaQ7u/A6Y{h3[ttmy]㿨F2J̆FmSQU'߄@Mz *@Zaloյm YV1OZ/Sd6m6JU,T,K#v\P<>]o쯣FgCjl+2W?rxZj6k}'ν"ul]+>UtVgz3y,/wr,vG%X(w= SVVT;(cLhyuW.joxٶ'?=&.:57l_f^b{}6m ~x1z M6ه}D׼^_kB~߾o ^S/͵?aaQ7u/A׿᩿fП0福mO{M]{ujoxٶ'?=&.:57l_f^bk-:Wb?bv&"hc`2qԙ‰jTnRFV=`c$]:G2ǟ[,Ǫf3iwd}:wd4%j )gwHXE1K2>Jj8d_CE>~ǦL|GT>v -RUrFՑiRc՗WYySu-OS7.53xn=`v/!Zz^#I5~K#[;QQE$= =x'ZX&vCVḷ dy>%#3dȖJY%8j>oELp'[u7Wm[FM="5ml UVZ* .j$ IzhXI,뼾uqY]}}^K۝6V?ѶT;$ Q<#i$*uXa ¤~?ឿ?ž7Nz3׿ឿ?žS7!nE^z\o}L={s;[Oپ3^gxo?f_xz3׿ឿ?žS7!nE^z\o}L={s;[Oپ3^gxo?f_xz3׿ឿ?žS7!nETt'16Ҭ8wsnz_rx(r4[/SUqdBi@uӤiq2Ǿ沕eq5RXy*++'y,A FOzYxt`ӫ~iCl{oVzޟ!~>uj+K?G[{!\cxnǪDtj@'=5\Pc%TT(ٶt+5,ij$M]+bw M6ه}D׼^_kB~߾o ^S/͵?aaQ7u/A׿᩿fП0福mO{M]{ujoxٶ'?=&.:57l_f^b{}6m ~x1z M6ه}D׼]7_LOͷf;UEe! ~x1zMMZv=1tRVO.^ޠ&WȔ8qÀBNmG7*z0׺uEWYovݽcxiz],tK7ZV Ō:m (xT-Q/b$۷JEF]U*&y49gffD1zu}6m ~x1z M6ه}D׼^_kB~߾o ^S/͵?aaQ7u/A׿᩿fП0福mO{M]{ujoxٶ'?=&.:57l_f^b{}6m ~x1z M6ه}D׼^_kB~߾o ^S/͵?aaQ7u/Aj;i*hT,qE*STG5Q7x1zu]{bIGgZlnexKRJ2{^v!#ˌS%P|܇oKu{"OGߘYߘI|C[5-JfO6zjEI—ԁ^PQl_kB~/Y:57l_f^b{}6m ~x1z M6ه}D׼^_kB~߾o ^S/͵?aaQ7u/A׿᩿fП0福mO{M]{ujoxٶ'?=&.:57l_f^b#\ŷ:L{wqMu0մ2+c- ttdb yKN+^ Ӫqvg VLz݋2XO.O2TYfԠ VS{lS^G[LA޽{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{qC|[×W}ߗI.VZB%<ڷfgG]9׽u~{ߺ^׽u~{ߺ^׽u(o?/lv7|߅G?Y#}n_1Jc~u~?gQn>o36fPfv~秧6\L kSO22ȍtv/t[Ov}1璒v`ivrRIj9`QHu#Tjs:?= =AۛkvXgܛ+xc!į U ~BP.))Hg/칔in#`RI^u1OOX?T3QW7TISSLCc*TĮ UTu4k՘c6*dGz5fs⎖81L1?={ߺZ8OY{o] [S٘L&;[x]?cojX<0o-g 9 c}:G|]"]G^Z_]f)f&wxʤyb*F^hHJXK % /=m yL2j:aL-)Zǥ:>o˫gGKmݫc61MJrUe+I*$UUI$%[XҤt%fg},/վ/UO^n>6{ vI:h(6v&ddbI< Vu-6Gp˥?>S7)eۤsoI4U?1d6xf<2"7iEݛuzQW)7J:uK d x{Wi񟳤_z7*Nux?zlt^׽u~{ߺ^׽u~{ߺ^֯.ڟYoˤw\W~MgWIo=)5:2u o|b݇S{1O/Hl?˺_׽u~{ߺ^׽u~{ߺ^׽u~iߕž;b/QN'=t{^׺ӏ7+}vM?_ ''[{.^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^֜_'#?KOΐCqe/׺uD?[y_Az /Ϥ~]n9컥{ߺ^׽u~{ߺ^׽u~{ߺ@o&{kx>_?gZ{qg?τ}$^t{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^\oPy__klKտ/Ft?{O?_ٯNuUnx^M6]7mNMpNŃjgWUdrU;ƛu(RuI34*PT+>cVڟOSTރ@%toJ'Tރ@%toJ'Tރ@%toJ'Tރ@%toJ'Tރ@%toJ'Tރ~4 }O?Nܗf+q`VGF詫4tP A+$rItF%B']WRպ76{Sv> t}_o#jI/1f`F_:^#c| <aŭnzez%C s-EJ.Jsb( iEôml>//t9<ܗn,Ncw8<9!b%MC[LAUIU0>ˈ x <[8J/+`EIjtM(GH uP…pNi;xZ)T-y5/?UC9*NÓ1>&u^jۙ*閮lr2#Բ -eM43Rq'?MA.dg3~?||U;yϘȤ oiw2r[?b*}[Onyky!}նF ԻW)H] ܪϏcD43ՉYп}9:{KҎ{Bo@yٵ8!cj:ddeztS>tl`#Q*Y}']BGt[/p2;~ gEmLzzyѕK)O:r5p FZĽ${7T/o]+׺3|_=W{)0nf6OcAV?7sTQRҌ s[URC- xt ]_g{?cֽ5۸'A(YZL%X*>3uScJ:xQDekT$عp)A+>cV޾=OV{+>cV߾=O^{+>cV߾=O^Zf쮿^[G5vdv\١A[9j|&V%ILUidH ΥP*IOC1GW#7Owa]EڝQ5O=q;|<5 I6Z2\dUOM-zcLK!V^[ݔj`1ҥZ?(w1GeƮGT$WwtJB'>}"zSz@%toJ'Tރ@%toJ'Tރ@%toJ'Tރ!M*y+۪fb27 Y4Qlsٲ~5,<)u) ?O$d;Fj+0Uȋ%ffHD2l$*jfk}I>~]3 Cv߾_^h} ]E>o6 'ҙJA-5d^/˘Y|w̬)|ƦZ:{{^׺/P7_Krx-]~Γ]|'w?o;7w?7~%LuWoe+h%m.#E{?^)SOI4<E 2K,ƌ#i^=UQu7G>_VڏQT@J_{?T@J_{?T@J_{?T@J_{?T~+X~kmk!ϋJl<36nٲU4tCLQ cW hIN# ǂ{6R9>|z 1GCXKM[EP#~UhxEG(o?/lv7|߅G?Y#}n_1Jc~u~{8CaSjru~` :Jhz5-$+$QEb_~_髧?u[ZԾmo={Rs~YyK:j]g믿hoC׵/髧?u[^Ծmo={Rs~YyK:vηK[ _G:OWE5TмQ$^F )}K#ת=z#[?kV0{'=DŽ_h |&bh5N*DO>d)zܷoKu{8CaSjru~Tҟ)v^PTn,&Oٛ'MKgTTV2EOG,P%2$j"iUh:bI3C{;or ߣ?oꇡ[ܿ5qџ]{ꇡ+m-cIv~~QWm|Vw)7XW1 7KG <IGdKnэUsOհ{O{ߺT.`;B(7F+1[_ x`\$|^S-I,tk bsYj;!՟{cuXy?]to[-^׽u! ߟlf7~} qe/׺hnؠla-%g2tXbD0e|,YT=p:@+5tk7ZuӟCͭ_~އj_Q׿WN?6]}Cz}G^M]9?kؕH'F ^kz{ ^kߵWu+ӟz@=֯~ާi_A׿ЯNϥ?Z[z}Z?&_k]A+dۻzăZlm%3T,-+pBIj[߿/Ft?{M?_ٯNu(o?/lv7|߅G?Y#}n_1Jc~u~{{^?Ţ|ʃrGxhRୖJxgL]\K$~ f=34^ T?k*̎v==OIzL'.윎MdZʄW!"xˠmV\G쿥w{l$%l AM2IgWz|^}_wXg6~MQ; Խ[+YJ) nݸW*!]iZ33֠|["{EҾ{n/5Uj,ipSf%1=tƟ421ZqdZ-zn?!n.1.&,̦x0Tk,Re|R #"/2 'D,1[[?q*ZZjjz**x(iX%H*"˺_~{ߺ^wW=M_S5ewWmeLZjgTO61F$vާi_A+ӟz@=֯~ާi_A׿ЯNϥ?Z[z}AqQSujTu_\SS b)-)jmz{ ^kߵWt`0;jq{s đLvGId$i(!dp-ͅrzӷu{{^׺u{{^׺/P7_Krx-]~Γ]|'w?o;7w?7~k w30#~ܮц!ڡcv[,+O 1Yg7[}=/𓻾tiJyk7w#Mܘi?Rxh)SQd*:Pц#?Gs;[Oپu/Oz3vطޟ}f/?׼?_˟bz者7^"g=.o}7߾oC{S1Kmt&CW3GoJ A&ޛqEG7XSm+NZt4@4ufy> |`y׫p+# 5]u54!Sı({I?/k:76{Sv> t}_o#jI/1f`F_:^֜!X[_of)??:swKuerXKA_WGO=CM<œƦ?t҂\*#]|#Z=鋟>O=G#lsͽ/uI.8₦wL҂ {U%H54p4њ~~JOOCG^~~JOC׾G^~~JOC׾G^~~JOC׾G^~~JOC׾G^~~JOC׾GLsvJo-~tR[R z~Fco-'-Ͼ?F__JDul˧Ra>nj-~V`OT=-."L]+(w65IKZ)⮞RM,E-t~~JOCG^~~JOC׾G^~~JOC׾G^~~JOC׾G^~~JOC׾G^~~JOC׾GM0Y6Ebo~իUNDRoˋi}U$!Ͼ?C_k_SKc|ޛ{Ve; ~TcČبa8|nǐ0$$3I%8QF#OG]9׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u7(o<r/|[%jOC#:佧Vz~/k:ևMr}G?w'WCȓ ޽~io/o~{ߺ^ָ-ڿؾoˤ\ի-?!_c[_WP~_?Ook7;+} ~&Gݫ$7ct{ߺ^׽uC_kMf[w.S3vш/F~m ebU2e(c!9Ϥi5mBѻ39WX:9W* UӢO;UD{\OOV 7[&{EҾ{{^׺A]|g}=\ne9mǚ]q_oRI$U/\ҴpE,dBxV`u۝~gs5MAU4J=IC["8eR bK|Rij xIKh'5ę=Sջ:ig[x0Le8(KQ[Y6zܦRY*jT9,-f,śT(8z}{ߺ^׽u~{ߺ^g_';k[+7~εzJ? ]~ζ_ދG/#_u{_/a'muB_OQ>p_Ϥּ[hOhzY׽u~{ߺ^׽u~{ߺ^׽u~{ߺT% kNQ=k}nofnf:=Y:{{?B?'><LEegIna?ʛ x{ffgV힝׺u{qC|[×W}ߗI.MYU>2GUOW]ySO]g-I[K#ΩQIU *4r)V=Vz~{k:3ULT@ՔK*Yj)3;B*J e hfX݀Wh\)%ˬ7;+~ ~&Gݫ$7ct{ߺZq`?o1éqe/׺][||]qPo<^{ ⩞|$j D)5EjY!h"9Ds"=7)S!4=kC2K] ){m) i0G&>}L?%yG/O~:7^sdr?S߾M׿m漢Cxg?A+gG#=aP={O~:7^sdr?S߾MN̉jvܬ̱rY  a L~:7C|}{S|1 ŹwN΍awNQ>w^oBz撑kYhuyLf[\UTU)*PahBy0CyxJMr 0ƑE]c(Ph4@aPo92\?9)S^&Q̃W6G{׼ z3 ͳ0uo ?sl}L?{G92\?9)S^&Q̃W6G{׼ zE7a/wm)W mz? %P*hyjG xþm 8ZheQ>?)|}}ٝ_ܽ=.լo|Y]3t) TeY:5jpOl{KҞ{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{qC|[×W}ߗI.VZB%<ڷfgG]9ִ?ob>/#?:D~K}1fgF}_{^׺u ǝo^^ϋ~]$>ޭ[i|bgCjoO?mtZ†jc{]g.q_ϫ[ds7?_i%ѿ?W׺u{{^V%qsSPO5@)%Ůtfk[V2\^h#K3ŐUFi~qTEՅ(]fǴ~{jdm$)0GPahU"f!Կ %.(G^׽uϕ]~S ƏgLX,l)Mwulfhq1RI+LqFǨҧ\G 㭫~F_ :_@C^i;vGWK%..KLyQǣg׽u~{ߺ^׽u~{ߺAu̜F{yl'>޴ :e+eg{]wv:GKz-4~Yhzٷ y?GU|Wy>Zo˭=g^׽u~{ߺ^׽u~{ߺ^׽u~P(ɯ9E?gIoUd??e̜3t-}%GՄ*ow6?=훏훧a}XWzw{^׺u_[WJ|]{ib`$4OOI!ohj^ QՔ]R@]Sjc-<%`vja;V A: ?r[ߩ'8S3 ͳ0uoF%b*InzY7Fvϼ7o WXqǩPѵ]\i|D uer{:llm}>'1H)1|E8f> bTTMGjx ;{^׺ӏ7+}vM?_ ''[{.^׽u~{ߺ^׽u~{ߺ^׽u=[[hH(>P|P,ISIfq B>RT3)%R.Lb/[.^׽uo~V:يOz@OaN]{8o&ez@ܟn;췥{ߺZqm/~M_-ݎ{ّq?woKuo|݇l1'Hl˺_׽u}jriGv1:A~i3tٔ??޷[{{^N?Ce?nAm˺_׽u~{ߺ^׽u~{ߺ^׽t|:zZj^&~1M؃H܃~_?a]?OmD۟"VY4sj%1I=6欎WOI ur?P$o}GK_mKKzu{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u ǝo^^ϋ~]$>ޭ[i|bgCjoO?mtZ‰O[g/~|W׿M/m~{{^׺7wſ9{Wu{>-tzod!r^}?ktxӝj =]uGu>oV~[}FLҘ_/_{^׺u۷v흇syqgje^g?C(bi+*eHKAb=hOϸ7}]M֐noU̖k?}2)<cMʼn.ުYLb`Moy;LWً-!8SO]?'ڛF]ɟA?H{}eNU4h?K"F|<ν{{^Y+0|GwDK~B5~7 9Etmmw/~b CeE}V^á2U$Ʊ f-YAcɡ&jm":%4?W_7J:u{{^׺u{{^3~-d֛?gZ~{llk?g[h/oE/V~[/0T6:/O'~_(j8/k^-u'=,׺u{{^׺u{{^׺u{5'(|g5>ލzsvʷӳ}s~_^=ֽ&no_zMn߇=qt? N~{ߺ^׽u~{ߺ^׽u~{ߺZq`?o1éqe/׺WYIJʹ⦥K=ED̑C H fb@>׺WN?6]}Cz}G^M]9?k!==?PJ6{HӟCͭ_{އj_Q׿WN?6]}Cz}G^M]9?k-tzod!r^}?ktxӝkC &>ˁ#m;dI_o^?G4ڷzSkto}u{{^\oPy__klKտ/Ft?{O?_ٯNu(o?/lv7|߅G?Y#}n_1Jc~u~{{^-}}*'cqԕ }D4t44pE]mm]COKIKO<;*")f }u/'>pf;'3ٞsq \^qĸ줴 UM48JC". 26-鶾uXW~kRt d[kN.S=eJ>N1#~TĖyJaF3=׽uiQg9gWdY"fB# YH<{׺YGIi+)*bhjijWdtu*H {8Ý~@oj{=yafw6F`$>H04Sv0G2KICh|LvʆoۻS%ZڻbzlSY^]+A"qk:u{{^׺u{ϺNv#=WݓoZoj A};_m쿥_G?,Y}nx_=RO?ꄿ<|gk}࿟IxО{^׺u{{^׺u{{^׺uK d x{Wi񟳤_z7*Nux?zltZ„N|yę??Β|#7g[~Ӱd>ά+=;׽u~{ߺ^׽u~{ߺ^׽u~iߕžb/S^?=t{^?O#=5 mM$8̬4u:|M;۽/C;Gm}|WRPl5jS9]==jX:Xv7H 8=h[1]@ѿ7_V+z@ѿ7V}+z@ѿ7V}+z@ѿ7V}+z@ѿ7V}+z@ѿ7V}+z'twOGeHGT$jdJ ('>izҷ~z+puy~ߕ86bO->#%Eccv *9&gy]"Ie2NҘOs{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^\oPy__klKտ/Ft?{O?_ٯNu(./HN'A{jiL_ٯѽW׺u{qC|[×W}ߗI.VZB%<ڷfgG]9֯.ڟYoˤw\W~MgWIo=)5:2u{U?)LďYJ̎\X]'/@zk2+c!ՁWK֯HˤSͨi=XZ?'uw&>ދS5+%M$s1Ҭ@/G>d|(2U3q7t?N?us>t{^YeK>-|ZMC4{;SoT4%`F,b`d$ՒI:{I?!9 ݭ{n'UnZX޺X{cejֺyzh.S4Rn̺:n)[ǀ .ٌv{Eg15QWc2ج UC_CW<5T+) 촂 ׽u}e./)L GYH䬴9|MS7fSGP5+Y*lQ/YC 'ZpC9m IC²y#ҽ)*o!f W\u/$؝G:5fo\Ll=texW38䥬sS칔n#`PБo{^׺u{{^3~-d֛?gZ~{llk?g[h/oE/V~[/0T6:/O'~_(j8/k^-u'=,׺u{{^׺u{{^׺u{5'(|g5>ލzsvʷӳ}s~_^=ֽ&no_zMn߇=qt? N~{ߺ^׽u~{ߺ^׽u~{ߺZq`?o1éqe/׺ ̳!ucw/xqO=#3. V#OMX٪3RO#957 q]jq \j}(fqMCiIwCOqr96aP/8̃W6G{׼ z3 ͳ0uo ?sl}L?{G92\?9)S^&Q̃W6G{׼ z3 ͳ0uooB* u1ZVH0uo >wfܙ{L^y+&쯐{znB6OknkWnlg+E639[>FbMuCQ,YG>$,5BpO\n#={ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺ^׽u~{ߺZ†/jb[gſ.]poV>13ݎKyo~ΏshD'p?}_zGw?ut??>+ VOJb~΍^׽u~k ;mIu}Z·v9/iվޟ5:<~ε{ s ݮσ~]#⿟Wo+?DھK?Lٯ?їN׽u/|foE>%|}n ݛPG*Yle)j[ H[oQyO^>}+_FX/ p tdaev.'iv|brhi͛NI?%E~|>}ni|3>u~ka׿PE>+|gUYUvLEK%[Md%ZQ F2D59ydm#C=,?򲟯d۟(H|{墋-_Fm{o ǼZ7c]Cb.' Ei!{/op2nɣ8ٹvz&2jZʪT8vT(+d AQP}#C! T?N{vF| E~OZ*ΌK\brye"W^ѣ3 <2:[p,SIK :H9x AK{1fU:(}f{U>!!O֯_)^/;뿀}#>[hdswEL?$;K?6zvڻ ׋~]m K:u{{^׺u{{^׺u{{^ꄿ@M}//෇v:Mu}mlߗW!/Oug(GdǟIyh->ެ'S۾6ᵹll;C½ӽ{ߺ^׽u~{ߺ^׽u~{ߺ^֜!X[_of)??:swKu{{^׺u{{^׺u{8<'a>_j?r~{ߺZq`?o1éqe/׺Ӌvi?oWp>̇פmz_׽uo_罙Hۭ}{^N??-vϳqڏt?ܟn9컥{ߺZq֧'/wo_/[{.^֜_'#?KOΐCqe/׺uD?[y_Az /Ϥ~]n9컥{ߺ^׽u~{ߺ^׽u~{ߺ@o&{kx>_?gZ{qg?τ}$^t{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^\oPy__klKտ/Ft?{O?_ٯNu(./HN'A{jiL_ٯѽW׺u{qC|[×W}ߗI.VZB%<ڷfgG]9֯.ڟYoˤw\W~MgWIo=)5:2j3mӵxWw<7^ٹ7JFmkϭ{^\ҡtƽ/FvgXduAqI)] M-g_[[0_(K`7MvۨAQpH rb(fǏӇCN^t{^ꊿ }TʳFpQj7ۡC3e(t?O~/]?,p iA!ŒRcoA?`ZgR 7[0{Eν{'q7 CY;{OY L+-[2#cHPVi>|'t-CZC+0 yyYw^M[O,ݩzəSYqQK1"{\O^Vۏ[{GҮ{{^׺u>9__vO}iu'WoH~#uZ>igI?o ݮ}&ߗ[B{Cν{{^׺u{{^׺u{{^׺/P7_Krx-]~Γ]|'w?o;7w?7~k 9fZ/k,&:Ku Tᅪmn{77N:l^׽u~{ߺ^׽u~{ߺ^׽uo~V:يOz@OaN]{{^׺u{{^׺u{{^N??-vϳqڏt?ܟn9컥{ߺ^֜!X[_of)??:swKuݿO[!ruޗ~iŴ5v9fGkqe/׺ӏ+?}wv_ O'/[{.^֜?_G˺_׽u7ofSozwoKu{8Wnރc1[{.^׽u~{ߺ^׽u~{ߺ^{ɫ7 ?uWOָ!doI-~#uW-׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺۱?ugm]^n7fvyclov6MD#p,}&RPwUw %A?6({N}ո_YWBiwDR5xВ=%-t\PV>7M}G>_u[?:uoOOWnmΓgUw.h]ͺ7aUmxJ6QKM ufG !$RHf :3ӟ:37#`^9ib >^ iX) $qȬDˤ`Xp'4St{ߺ^׽uDϟw`'Xfg1]9NSKUW{Y ]*>YZ$*x\(:?+ˡ󤲟(w;þsv4Ru Y5"7*HSPD얺ܶWIsBҴ`"oB>_ocq gtܛk%``_7jyb6h?g4TI,q I?MI!ϺCaK]/=___u+&>ف#_G/^?:/%?kwW:󩺡:+L~k~RDq#hhGZ̥jd5T L 'jژr̴QNgVQgoI%|ENFhs1#K[+ A" VKz~7[){EҾ{{^׺K-i5yϗSU6gv> MF?!-CS{z $C}ׁ%7H??t/쾴{W`˶ha'aϟe3$cܴslLtʩ=x'+ $VjU[T 7u?/zGG@TȊ,JΡ 28K׾??[)v^ywmSUvMy3n2+EMJYJ4cBBCSk>z0׺u{{^-dLtSn}v5I.kWHTHֈZDO)?we&]cg3{?#DW5YwoIb} ƒ"΂z.G0VAc?WzWgTv~r;z}'0՛1UW\̄nZ9,ij=iu)Zqo[kllj>ɛf흉%u4ϸ2nZYI>"3TGPڠql 8jǁb_hU׽u~{ߺ^׽u~{ߺ^׽u~{ߺU!=OW8zǸc{n)wp&irC]KSR̨#"Ql$E:bKG Hfkо,.ͯsԻ%c6 3d&;xSt)Il$}u='GF_gFWom/|a_(v{k9_Q[V-.[:l]4P8e?$GaMI)m7@|NΤݺum=)"<~{=lB@Z IB'R$V!aFP)NO^׽u~{ߺ^׽u~{ߺ^׽u/?R|l۹]ֹ*z.͛)idUUB@pL`+$ف#G/^?:M}G߾_ufЏ}={7~?/zG׿"oB>_cD0?}W^`K׾?Ag}_{&>ف#G/^?:/(S112K߭4I)R#y!N^Ĩt,8 >_cvdc&j0?c;˴{rTEyǥݣ1uU+:=en!y Kc^?Ϣwnct&3xP6ew7cgttSo+MZQSCO$VQMS!91XJi&Z/ޗ~ij?_NmK {JNLZy d7&լWTQT@$8*%"ãΔ= 4rz5 zF`U-nHS'so}c oi>08jVjUSPQ2MϤdYbgP@u&F?u?WvɟODclnh&ش{SL ՟7kWIN枂 *E8@xgt{^Jϑ4]?&vy;+{]6p񋐡cU$,/ 3M3ACt^WϣA /ͳS+7Ϫ?_  (|dyhj#5hSWAOqs-ĕ4!:yTI!}qb1i'܍BԘ[:uF QLX~^()ukUOZVg.xuw~{ߺZ/tgw]gO1r 9z vf ف#G/^?:M}G߾_ufЏ}={7~?/zG׿"oB>_cD0?}WAq>ܯguGcuկ/rYsa-]*~2d'v l+ [Ri}?MuU / ?Z..u؛*j4،~SrqhV.sKJZ0uӸ`hVR*LJ[#EҾ{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{{^׺u{YY}|gfw`߸?gQ~tTzۭuu_ȣ#p<gO˭t{^׺u{YY}|gfw`߸?gQ~tTzۭu|_/3u_zSk񟳭{^_p5mڎ#֍z.׺4? 쫺{n_sGF{ߺ^׽u~EU QZߺ_:ڟ|/~tTz}۪~nxneFَ~׺u{{^׺+ 3W?ޑ]|C_k:KoxuǯGX̴11ɛ?oFϽu{{^׺u{{^׺u{{^׺uaX]v͡~΋%L=}]&ፕ,_}(+wKu{{^׺u{{^׺u{{^'ḣ]1qJ/f}~{ߺ^׽u~{ߺ^׽u~{ߺ[&!^~=f:lt^׽tP>z%7Csi}I ?mofߺ[-$ne3hz3{k:uEa"On驿=hۢޜxʇr8g$hmW=G^X8O#kQmLIf}t?9p_íd?cbY=I񟷣DхN׽u~kU }Q9Sm;.{ߺ^׽u~{ߺ^׽u~{ߺNXobeCQ{^z*Wn{GE#l:׭~{ߺ^/<@< 8NormalPJ_HmH sH tHb@b 8 Overskrift 2$<@& 56CJOJQJ\]^JaJ\@\ M Overskrift 3$<@&5CJOJQJ\^JaJNA@N Standardskrifttype i afsnitViV Tabel - Normal4 l4a 6k@6Ingen oversigtZOZ ALMS overskrift 2 <5;CJOJQJaJROR 8 Tegn056CJOJQJ\]^J_HaJmH sH tH:O: f` LMS txtOJQJmH sH FO"F !+LMS txt + Centered$a$616_0 LMS Bulleted FHOBH ALMS overskrift 1 CJ \aJ HOQH f` LMS txt CharOJQJ_HmH sH tH8b8 $jvKommentartekstJOrJ DM LMS footerB*CJaJmH phsH JOJ ALMS overskrift 3;CJ\aJhOh ALMS overskrift 2 Char&5;CJOJQJ_HaJmH sH tHFOF ALMS overskrift 3 Char\POP `HLMS overskrift <5CJOJQJZOZ `HLMS forside overskrift$a$ CJ<mH sH jOj hOverskrift 2 Tegn056CJOJQJ\]^J_HaJmH sH tH6U@6 h Hyperlink >*B*ph:@: _H Sidehoved  9r vv MTabel - Gitter7:V 0 PJFORF MLMS txt + Centered TegnTg@!T F6HTML-skrivemaskineCJOJPJQJ^JaJTT / Indholdsfortegnelse 2 #^5\NN / Indholdsfortegnelse 3 $^p@p / Indholdsfortegnelse 1% % x5;CJOJQJ\^JaJNN F6Indholdsfortegnelse 4 &^NN F6Indholdsfortegnelse 5 'X^XNN F6Indholdsfortegnelse 6 ( ^ NN F6Indholdsfortegnelse 7 )^NN F6Indholdsfortegnelse 8 *^NN F6Indholdsfortegnelse 9 +x^x6 @6 vhSidefod , z&@"@ O` Billedtekst -xx5\FF f Ballontekst.CJOJQJ^JaJ,-.,-.1  !B23*Ex5 %0:BKT[cvVWbjs   * 4 > F P [ c t }    ) 4 > H P Z d o z  ! ( 3 ? K U ` k x   ( 4 @ I R \ e t {  + ; K [ k { mny>?KWblw #-8CMWXYZ[\_`abd:O000x%0x%0x%0x%0x%0x%0%00x000x0x0x0x0x0x0x0x0x0x0x 0x 0x 0x 0x 0 00 0 00 00 0 x 0 0 0 x 0 x 0  0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x0x0x0x0x 0x 0 0 00 00 0x 00 00 0x 0 0 0 x 0 0 0 x 0 0 0 00 0x 0x 00 00 00 00 0x 0x 0x 0 0x 0 00 0x 0 00 0 x 0!x 0" 0#x 0$x 0%x 0&x 0' 0(x 0)x 0* 0+x 0,x 0-x 0.x 0/x 00x 01x 02x 03x 04x 05 06x 07x 08x 090 0:x 0;0 0<x 0=0 0>x 0?0 0@0 0A0 0B0 0C0 0D0 0E0 0F0 0Gx 0H0 0Ix 0Jx 0Kx 0L 0Mx 0Nx 0Ox 0Px 0Qx 0Rx 0Sx 0T 0U 0V 0Wx 0Xx 0Y 0 0 0 0x 0x 0x 0x 0x 0 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0 0x 0x 0x 0x 0x0x0x0x 0x 0x 0x 0x 0x 0x0x0x0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0 0x 0x 0x 0x 0x 0x 0x 0x 0x0x0x0x0x0x@0@0@0P@0@0@0@0@0P@0@0@0P@0@0@0@0@0@0P0 X 0x00P0P0P0P0H0P0H0PA0,\d:O0000000A0,^~00  @0^~00 @0@0 0  0 @0 @0A0,TX[^a c Pz;b[3 %'(*Ja} $@BCEe X%X%X%X%X%X%X%̕HOQ^!$   _R$(S0ЯcOBPFr7Bb$[1-0)Bb$`D mY?kb$Esku7NI'"$b2@  &(   )F: 3  s"*?`  c $X99?)F:t  C 8A Background_grey)*8n  C "`-)  B S  ? (  NB  S DQ\   3  "`    `BS%CDEFS%@#" \   3  "` D%4tTX^ D%uD%u %u %vu _Toc135640904 _Toc138817363 _Toc141076595 _Toc141076596 _Toc141076597 _Toc141076598 _Toc141076599 _Toc141076600 _Toc141076601  013 09:ABJKSTZ[bcuvWabijrs~      ) * 3 4 = > E F O P Z [ b c s t | }       ( ) 3 4 = > G H O P Y Z c d n o y z   ! ' ( 2 3 > ? J K T U _ ` j k w x ?JKVWabklvw  "#,-78BCLMV\\35WijH I Q R n?WZ[\\Ji9rf *lx)qT{šg.c{.̺3W\ *(,r˚>s/f~B3";/#Je6OoQt!U C^\RaZjct;g+eiqe2oG .zq\Cj`Sr#u wH}^`OJPJQJ^Jo(-^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJPJQJ^Jo(-^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`o(. ^`hH. pLp^p`LhH. @ @ ^@ `hH. ^`hH. L^`LhH. ^`hH. ^`hH. PLP^P`LhH.^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJPJQJ^Jo(-^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJPJQJ^Jo(-ee^e`OJQJ^Jo(hHo55^5`OJQJo(hH  ^ `OJQJo(hH  ^ `OJQJ^Jo(hHo^`OJQJo(hHuu^u`OJQJo(hHEE^E`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH" pp^p`OJQJ^Jo(hHo@ @ ^@ `OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hHPP^P`OJQJ^Jo(hHo  ^ `OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJPJQJ^Jo(-^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH88^8`OJQJo(hH" ^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hHh^`OJQJo(hHh^`OJQJ^Jo(hHohpp^p`OJQJo(hHh@ @ ^@ `OJQJo(hHh^`OJQJ^Jo(hHoh^`OJQJo(hHh^`OJQJo(hHh^`OJQJ^Jo(hHohPP^P`OJQJo(hHh^`OJQJo(hHhpp^p`OJQJ^Jo(hHoh@ @ ^@ `OJQJo(hHh^`OJQJo(hHh^`OJQJ^Jo(hHoh^`OJQJo(hHh^`OJQJo(hHhPP^P`OJQJ^Jo(hHoh  ^ `OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJPJQJ^Jo(-^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hH^`OJQJhH^`OJQJ^Jo(hHopp^p`OJQJo(hH@ @ ^@ `OJQJo(hH^`OJQJ^Jo(hHo^`OJQJo(hH^`OJQJo(hH^`OJQJ^Jo(hHoPP^P`OJQJo(hHJi .zqe6O;g+eSr/#J\Rag. C^#u!UB39Zjc>s/c{\ *3W";*)q(,iqerf H} woQQ>        p                 J@p        lT        /                 3        /                          h        s&( M+77#FIKpKpKru >4|"fgN>SUQw0@|`@Unknowng: Times New RomanTimes New Roman5SymbolA& : Arial arialG5  hMS Mincho-3 fgi&  '@ChaletOfficeLucida Sans Unicode?5 z Courier New5& zaTahoma;Wingdings"AhjIfj q q !nSr4dTT_ 3qH)?;q0#LEGO MINDSTORMS NXT Direct CommandsFlemming Bundgaarddkflebunx                Oh+'0   4@ \ h t $LEGO MINDSTORMS NXT Direct CommandsEGOFlemming Bundgaard lemlem Normal.dotn dkflebuntn11lMicrosoft Word 10.0@@R]@DiU@nk#q ՜.+,D՜.+,\ hp   LEGO Company9T: $LEGO MINDSTORMS NXT Direct Commands Titel| 8@ _PID_HLINKSA4*1)_Toc1410766011#_Toc1410766002_Toc1410765992_Toc1410765982_Toc1410765972 _Toc1410765962_Toc141076595  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Root Entry F #Data 1Table=]WordDocumentSummaryInformation(DocumentSummaryInformation8CompObjj  FMicrosoft Word-dokument MSWordDocWord.Document.89qnxt-firmware-1.29.7/LEGO Open Source License.doc000066400000000000000000001200001466344546000211250ustar00rootroot00000000000000ࡱ> HJI@ Lbjbjצצ"\D  $ b        $RQB  B  W       ø  m 0    B B   LEGO OPEN SOURCE LICENSE AGREEMENT 1.0 LEGO MINDSTORMS NXT FIRMWARE This LEGO Open Source License Agreement is an open source license for the firmware of the LEGO MINDSTORMS NXT microprocessor. IMPORTANT -- READ CAREFULLY: THIS AGREEMENT ONLY COVERS THE FIRMWARE OF THE LEGO MINDSTORMS NXT MICROPROCESSOR AND DOES NOT COVER ANY "SOFTWARE" AS DEFINED IN THE MINDSTORMS NXT END USER LICENSE AGREEMENT (HEREINAFTER THE " MINDSTORMS NXT EULA"). Section 1. Definitions. "Contributor'' means each entity that creates or contributes to the creation of Modifications. ''Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. ''Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. ''Executable'' means Covered Code in any form other than Source Code. "Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. ''License'' means this document. ''Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is (a) any addition to or deletion from the contents of a file containing Original Code or previous Modifications, or (b) any new file that contains any part of the Original Code or previous Modifications. "Original Code" means Source Code for the firmware of the LEGO MINDSTORMS NXT microprocessor, and which, at the time of its release under this License is not already Covered Code governed by this License. ''Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. "You'' (or "Your")means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. Section 2. Source Code License and Larger Works. 2.1. LEGO Grant. LEGO grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work. 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive licenseto use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work. 2.3. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. Section 3. Distribution Obligations. 3.1. Application of License. The Modifications which You create or to which You contribute shall be governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via a generally accepted mechanism for the electronic transfer of data (hereinafter "Electronic Distribution Mechanism"), to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by LEGO and including the name of LEGO in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. 3.4. Intellectual Property Matters. (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You must distribute the Executable version of Covered Code under the terms of this License. Section 4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. Section 5. Exclusions. Notwithstanding anything to the contrary herein, no Software (as defined in the Mindstorms NXT EULA) shall be released or made available as open source under this License, regardless of how any Covered Code interacts with any Software (as defined in the Mindstorms NXT EULA). Section 6. Versions of the License. 6.1. New Versions. LEGO may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by LEGO. No one other than LEGO has the right to modify the terms applicable to Covered Code created under this License. Section 7. Disclaimer of Warranty. ALL COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT LEGO OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. Section 8. Termination. 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against LEGO or a Contributor (LEGO or Contributor against whom You file such action is referred to as "Participant")alleging that such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i)agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. Section 9. Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, LEGO, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. Section 10. U.S. Government End Users. The Covered Code is a ''commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ''commercial computer software'' and ''commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. Section 11. Responsibility for Claims. As between LEGO and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with LEGO and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. Section 12. Trademarks. This License does not grant, nor shall any provision of this License be construed as granting, any rights or permission to use the trade names, trademarks, service marks, or product names of LEGO. Section 13. Governing Law To the extent possible under applicable law, this License shall be governed by Danish law and shall be subject to the non-exclusive jurisdiction of the Commercial and Maritime Court of Copenhagen. This License will not be governed by the United Nations Convention of Contracts for the International Sale of Goods, the application of which is hereby expressly excluded. You acknowledge that the export of any Covered Code is governed by the export control laws of the United States of America and other countries. If you are downloading any Covered Code, You represent and warrant that You are not located in or under the control of any country which the export laws and regulations of such country or of the United States prohibit the exportation of the Covered Code. Section 14. Miscellaneous. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. EXHIBIT A - LEGO Open Source License Agreement The contents of this file are subject to the LEGO Open Source License Agreement Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.______________.com Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. LEGO is the owner of the Original Code. Portions created by _________________ are Copyright (C) _________. All Rights Reserved. Contributor(s): ______________________________________. G F I A X T _ v &Bu"j|rlo 9p""##%%@%׽h0g0JCJOJQJ^J h0g0J5CJOJQJ\^Jh0g5PJ\h0gPJaJh0g5PJ\aJ h0g>* h0gPJh0g h0g5\E(GH A T v jrp ^```$a$Lp"#%q'*Y*Z*,,.,.-...i0j0005363O3P3^` ^` ^``@%I%&&q''''((*W*Z*++,,,-8----...*.-.?...j00063M3P3V344u9{9P;T;O<s<v<@+@.@A BB|CC_D{DGGGI I!IDIrIILλȻĴ h0g5\h0g5PJ\h0g h0gPJh0g0JCJOJQJ^J h0g0J5CJOJQJ\^Jh0gPJaJh0g5PJ\aJBP34u9P;O<u<v<@@-@.@AA BB|C}CCC^D_D{D|DGGGGI^``IIIEI:JK[KKL^$a$^ 1h/ =!"#$%@@@ NormalCJ_HaJmH sH tH NA@N Standardskrifttype i afsnitZi@Z Tabel - Normal :V 44 la 6k@6Ingen oversigt B^@B Normal (Web)dd[$\$Tg@T HTML-skrivemaskineCJOJPJQJ^JaJD\(GHATv j rpq"Y"Z"$$&,&-&&&i(j(((5+6+O+P+,u1P3O4u4v488-8.899 ::|;};;;^<_<{<|<????AAAEA:BC[CCD?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0000000000000000000000000000000000000000000000000000000000000000000000@%L'+pP3IL(*,-L)DDRJ0g@DT D`@Unknowngz Times New RomanTimes New RomanUSymbolTimes New RomanG& z ArialHelveticaW5  z Courier NewCourier New"hcfcf&) 9"{) 9"{qr4dCC3QH?RJ#LEGO OPEN SOURCE LICENSE AGREEMENTCOBriendkpetstrOh+'0 (4 P \ h t$LEGO OPEN SOURCE LICENSE AGREEMENTEGOCOBrienOBrOBr Normal.dotS dkpetstrtS2peMicrosoft Word 10.0@@@x@x) 9Root Entry FnwHM@Data /1Table7WordDocument"\  !"#$%&'()*+,-.012345689:;<=>?ABCDEFGLNK  Source on the firmwear. SummaryInformation(@DocumentSummaryInformation8CompObjj  FMicrosoft Word-dokument MSWordDocWord.Document.89q՜.+,D՜.+,` hp  Cantor Colburn LLP{"C: $LEGO OPEN SOURCE LICENSE AGREEMENT Titel`H<DLTX_AdHocReviewCycleID_EmailSubject _AuthorEmail_AuthorEmailDisplayName_PreviousAdHocReviewCycleID|n"peter.strandgaard@europe.lego.comPeter StrandgaardA Open Source EULA and License for Opennxt-firmware-1.29.7/LICENSE000066400000000000000000000027771466344546000152730ustar00rootroot00000000000000NXT Improved Firmware combines code covered by the LEGO Open Source License with code covered by various other open source licenses. Please see the enclosed "LEGO Open Source License" file for more precision on this license conditions. Details: - src/*: LEGO Open Source License. - src/d_sound_adpcm.r: Permissive license, see header. - data/*: LEGO Open Source License, converted from original files. - lib/*: Expat license, see header. - include/*: see header (provided "as is"). - startup/*: see header (provided "as is"). - armdebug/*: see armdebug/README (dual, LEGO Open Source License or GPLv2). - tools/*: Expat license, see header. - contrib/*: see header. - tests/*: Expat license, see header. LEGO(R) is a trademark of the LEGO Group of companies which does not sponsor, authorize or endorse NXT Improved Firmware. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nxt-firmware-1.29.7/Makefile000066400000000000000000000077301466344546000157200ustar00rootroot00000000000000BASE = . SRCDIR = $(BASE)/src LIBDIR = $(BASE)/lib DBGDIR = $(BASE)/armdebug/Debugger CPUINCDIR = $(BASE)/include STARTUPDIR = $(BASE)/startup TOOLSDIR = $(BASE)/tools DATE_FMT = +%Y-%m-%dT%H:%M ifndef SOURCE_DATE_EPOCH SOURCE_DATE_EPOCH = $(shell git log -1 --pretty=%ct) endif BUILD_DATE ?= $(shell LC_ALL=C date -u -d "@$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null \ || LC_ALL=C date -u -r "$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null \ || LC_ALL=C date -u "$(DATE_FMT)") TARGET = nxt_firmware # Set to 'y' to enable embedded debuger. ARMDEBUG = n ARM_SOURCES = THUMB_SOURCES = c_button.c c_cmd.c c_comm.c c_display.c c_input.c c_ioctrl.c \ c_loader.c c_lowspeed.c c_output.c c_sound.c c_ui.c \ d_bt.c d_button.c d_display.c d_hispeed.c d_input.c \ d_ioctrl.c d_loader.c d_lowspeed.c d_output.c d_sound.c \ d_timer.c d_usb.c \ m_sched.c \ sscanf.c \ Cstartup_SAM7.c ASM_ARM_SOURCE = Cstartup.S ASM_THUMB_SOURCE = BITMAPS = Cursor Display Fail Info LowBattery Ok Wait \ RCXintro_1 RCXintro_2 RCXintro_3 RCXintro_4 RCXintro_5 RCXintro_6 \ RCXintro_7 RCXintro_8 RCXintro_9 RCXintro_10 RCXintro_11 \ RCXintro_12 RCXintro_13 RCXintro_14 RCXintro_15 RCXintro_16 \ Test1 Test2 ICONS = Connections Devices Font Icons Port Running Status Step MENUS = Mainmenu \ Submenu01 Submenu02 Submenu03 Submenu04 Submenu05 Submenu06 Submenu07 vpath %.c $(SRCDIR) vpath %.c $(LIBDIR) vpath %.c $(STARTUPDIR) vpath %.S $(STARTUPDIR) INCLUDES = -I$(BASE) -I$(CPUINCDIR) MCU = arm7tdmi STARTOFUSERFLASH_DEFINES = -DSTARTOFUSERFLASH_FROM_LINKER=1 VERSION_DEFINES = -D'BUILD_DATE="$(BUILD_DATE)"' DEFINES = -DPROTOTYPE_PCB_4 -DNEW_MENU -DROM_RUN -DVECTORS_IN_RAM \ $(STARTOFUSERFLASH_DEFINES) $(VERSION_DEFINES) OPTIMIZE = -Os -fno-strict-aliasing \ -ffunction-sections -fdata-sections WARNINGS = -Wall -W -Wundef -Wno-unused -Wno-format THUMB_INTERWORK = -mthumb-interwork SPECS = --specs=picolibc.specs CFLAGS = -g -mcpu=$(MCU) $(THUMB) $(THUMB_INTERWORK) \ $(WARNINGS) $(OPTIMIZE) $(SPECS) ASFLAGS = -g -mcpu=$(MCU) $(THUMB) $(THUMB_INTERWORK) \ $(SPECS) CPPFLAGS = $(INCLUDES) $(DEFINES) -MMD LDSCRIPT = $(LIBDIR)/nxt.ld LDFLAGS = -nostdlib -T $(LDSCRIPT) -Wl,--gc-sections LDLIBS = -lc -lm -lgcc ifeq ($(ARMDEBUG),y) ASM_ARM_SOURCE += abort_handler.S undef_handler.S debug_hexutils.S \ debug_stub.S debug_comm.S debug_opcodes.S \ debug_runlooptasks.S vpath %.S $(DBGDIR) DEFINES += -DARMDEBUG INCLUDES += -I$(DBGDIR) endif CROSS_COMPILE = arm-none-eabi- CC = $(CROSS_COMPILE)gcc OBJDUMP = $(CROSS_COMPILE)objdump OBJCOPY = $(CROSS_COMPILE)objcopy FWFLASH = fwflash ARM_OBJECTS = $(ARM_SOURCES:%.c=%.o) $(ASM_ARM_SOURCE:%.S=%.o) THUMB_OBJECTS = $(THUMB_SOURCES:%.c=%.o) $(THUMB_ARM_SOURCE:%.S=%.o) OBJECTS = $(ARM_OBJECTS) $(THUMB_OBJECTS) BITMAPS_SOURCES = $(BITMAPS:%=%.h) ICONS_SOURCES = $(ICONS:%=%.h) MENUS_SOURCES = $(MENUS:%=%.h) all: bin elf: $(TARGET).elf bin: $(TARGET).bin sym: $(TARGET).sym lst: $(TARGET).lst $(TARGET).elf: THUMB = -mthumb $(TARGET).elf: $(OBJECTS) $(LDSCRIPT) $(LINK.c) $(OBJECTS) $(LOADLIBES) $(LDLIBS) -o $@ %.bin: %.elf $(OBJCOPY) --pad-to=0x140000 --gap-fill=0xff -O binary $< $@ %.sym: %.elf $(OBJDUMP) -h -t $< > $@ %.lst: %.elf $(OBJDUMP) -S $< > $@ $(THUMB_OBJECTS): THUMB = -mthumb -include $(OBJECTS:%.o=%.d) LAST_BUILD_DATE=none -include version.mak ifneq ($(LAST_BUILD_DATE),$(BUILD_DATE)) .PHONY: version.mak version.mak: echo "LAST_BUILD_DATE = $(BUILD_DATE)" > $@ endif c_ui.o: version.mak c_ui.o: $(BITMAPS_SOURCES) $(ICONS_SOURCES) $(MENUS_SOURCES) %.h: data/bitmaps/%.toml data/bitmaps/%.png python3 $(TOOLSDIR)/img2src -o $@ $^ %.h: data/icons/%.toml data/icons/%.png python3 $(TOOLSDIR)/img2src -o $@ $^ %.h: data/menus/%.toml python3 $(TOOLSDIR)/menu2src -o $@ $< program: $(TARGET).bin $(FWFLASH) $(TARGET).bin clean: rm -f $(TARGET).elf $(TARGET).bin $(TARGET).sym $(TARGET).lst \ $(OBJECTS) $(OBJECTS:%.o=%.d) version.mak \ $(BITMAPS_SOURCES) $(ICONS_SOURCES) $(MENUS_SOURCES) nxt-firmware-1.29.7/README000066400000000000000000000037741466344546000151440ustar00rootroot00000000000000This is NXT Improved Firmware, an open source community driven work based on the original LEGO Mindstorms NXT firmware. Online resources are accessible on the NXT Improved Firmware web page, including documentation, building guides, and more: http://nxt-firmware.ni.fr.eu.org/ Quick start ----------- To build the firmware, run make in this directory. It will produce a nxt_firmware.bin file that you can flash with your tool of choice. For example, you can use fwflash from libnxt. License ------- NXT Improved Firmware combines code covered by the LEGO Open Source License with code covered by various other open source licenses. Please see the enclosed "LEGO Open Source License" file for more precision on this license conditions. Details: - src/*: LEGO Open Source License. - src/d_sound_adpcm.r: Permissive license, see header. - data/*: LEGO Open Source License, converted from original files. - lib/*: Expat license, see header. - include/*: see header (provided "as is"). - startup/*: see header (provided "as is"). - armdebug/*: see armdebug/README (dual, LEGO Open Source License or GPLv2). - tools/*: Expat license, see header. - contrib/*: see header. - tests/*: Expat license, see header. LEGO(R) is a trademark of the LEGO Group of companies which does not sponsor, authorize or endorse NXT Improved Firmware. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nxt-firmware-1.29.7/armdebug/000077500000000000000000000000001466344546000160375ustar00rootroot00000000000000nxt-firmware-1.29.7/armdebug/.gitignore000066400000000000000000000007351466344546000200340ustar00rootroot00000000000000# Ignore tag MASTER-REPO_DO-NOT-DELETE *.lst *.objdump .DS_Store # Generally annoying things. *.[oa] *.pyc *.bin *.elf *.rxe *.map *.orig *.log *~ *.swp \#*\# .\#* # Python distutils creates this when building. pynxt/build/ # XCode build stuff FantomModule/build/ *mode1v3 *pbxuser # SCons cruft .sconsign.dblite .sconf_temp build_flags.py # Precommit hooks drop a commit.msg file if they fail. commit.msg # The option-cache scons.options # pyfantom related pyfantom.py nxt-firmware-1.29.7/armdebug/.project000066400000000000000000000003111466344546000175010ustar00rootroot00000000000000 armdebug nxt-firmware-1.29.7/armdebug/AUTHORS000066400000000000000000000004111466344546000171030ustar00rootroot00000000000000The following people have contributed code, in various quantities, to armdebug. A big thanks to all of them, armdebug is what it is in part thanks to each of them. In alphabetical order: Nicolas Schodet Tat Chee Wan nxt-firmware-1.29.7/armdebug/COPYING000066400000000000000000000005731466344546000170770ustar00rootroot00000000000000The armdebug project is dual licensed. You can either use the GNU GPLv2 license in GNU-GPLv2.txt, or else the "LEGO Open Source License" in LEGO_Open_Source_License.doc to redistribute the code. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. nxt-firmware-1.29.7/armdebug/Debugger/000077500000000000000000000000001466344546000175635ustar00rootroot00000000000000nxt-firmware-1.29.7/armdebug/Debugger/_c_arm_macros.h000066400000000000000000000032161466344546000225220ustar00rootroot00000000000000/** @file _c_arm_macros.h * @brief Define macros to support shared C and ASM headers * */ /* Copyright (C) 2010 the NxOS developers * * Module Developed by: TC Wan * * Thanks to Bartli (forum post @ embdev.net ARM programming with GCC/GNU tools forum) * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #ifndef __C_ARM_MACROS__ #define __C_ARM_MACROS__ #ifdef __ASSEMBLY__ #define NULL 0x0 #define FALSE 0 #define TRUE ~FALSE #define TYPEDEF @ #define FUNCDEF @ .set last_enum_value, 0 .macro enum_val name .equiv \name, last_enum_value .set last_enum_value, last_enum_value + 1 .endm #define ENUM_BEGIN .set last_enum_value, 0 #define ENUM_VAL(name) enum_val name #define ENUM_VALASSIGN(name, value) \ .set last_enum_value, value ;\ enum_val name #define ENUM_END(enum_name) #else /* C Defines */ /** Macro to control typedef generation * */ #define TYPEDEF typedef /** Macro to control extern generation * */ #ifndef FUNCDEF #define FUNCDEF extern #endif /** Macro to control typedef enum generation * */ #define ENUM_BEGIN typedef enum { /** Macro to specify enum instance (auto value assignment) * */ #define ENUM_VAL(name) name, /** Macro to control enum specification and value assignment * */ #define ENUM_VALASSIGN(name, value) name = value, /** Macro to control enum named type generation * */ #define ENUM_END(enum_name) } enum_name; #endif /* Example of how to use the ENUM definition macros ENUM_BEGIN ENUM_VAL(INIT) ENUM_VAL(RESET) ENUM_VAL(CONFIGURED) ENUM_END(enum_label) */ #endif /* __C_ARM_MACROS__ */ nxt-firmware-1.29.7/armdebug/Debugger/abort_handler.S000066400000000000000000000102131466344546000225100ustar00rootroot00000000000000 /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * Redistribution of this file is permitted under * the terms of the GNU Public License (GPL) version 2. */ #define __ASSEMBLY__ #include "debug_stub.h" #include "debug_internals.h" #define PREFETCH_OFFSET 4 #define DATA_OFFSET 8 /* Trap Abort Exceptions. * On triggering, lr (R14) contains the previous mode's pc (R15). * Based on example in Hohl, "ARM Assembly Language: Fundamentals and Techniques" * Chapter 11, Example 11.1. */ /* * NOTE: This routine closely mirrors the undef_handler routine, since we will store * the ABORT stack frame in the UNDEF stack. * In addition, since ARMDEBUG uses Abort mode, if the ABORT occurs while the * debugger is running, the value of SP_abort is not valid. This should not happen * in any case (it is a BUG if ARMDEBUG triggers an ABORT). * * We assume that the DEBUG stack holds only one stack frame and we will overwrite it. * On entry, LR_undef contains the PC+4 for Prefetch Abort, and PC+8 for Data Abort. * * For the purpose of Debugging, the stack frame should present the PC (R15) as the address * of the instruction that triggered the Abort. Hence we need to adjust R15 * to point to the address of the ABORTed instruction. * * We will also store ABORT LR (next instruction pointer) and ABORT SPSR to the stack. * * For the handler, once the user registers have been stored in the DEBUG stack, the * registers will be used as follows: * * R0: ABORT LR, then ABORT instruction address * R1: SPSR * R2: PC Offset, then Mode * R3: DEBUG Stack Pointer (for Banked R13-R14 update) * R4: Abort Type Enum */ .text .code 32 .align 0 .extern dbg__display_abort_info .extern dbg__abort_exception_handler .extern default_prefetch_abort_handler .extern default_data_abort_handler dbg_interwork prefetch_abort_handler ldr sp, =__debugger_stack__ stmfd sp, {r0-r15}^ /* Save workspace, previous mode's pc via 'S' flag, R13-R15: placeholders */ mov r2, #PREFETCH_OFFSET mov r4, #DISP_ABORT_PREFETCH /* Display Abort Info Type */ mov r5, #DBG_ABORT_PREFETCH /* Debugger Abort Type */ b _common_abort_handler dbg_interwork data_abort_handler ldr sp, =__debugger_stack__ stmfd sp, {r0-r15}^ /* Save workspace, previous mode's pc via 'S' flag, R13-R15: placeholders */ mov r2, #DATA_OFFSET mov r4, #DISP_ABORT_DATA /* Display Abort Info Type */ mov r5, #DBG_ABORT_DATA /* Debugger Abort Type */ _common_abort_handler: sub r0, lr, r2 /* R0: Adjust PC to ABORTed instruction address */ str r0, [sp, #-4] /* Save ABORTed Instruction PC to stack (R15 slot) */ sub r3, sp, #4 /* Use R3 to write Banked R13-R14 of ABORT instruction, update R3 to point to R14 slot */ mrs r1, spsr /* Copy SPSR to r1 */ tst r1, #CPSR_THUMB /* Check for Thumb Mode */ addne r0, r0, #2 /* Is Thumb instruction, adjust PC for ABORT next instruction address */ addeq r0, r0, #4 /* Is ARM instruction, adjust PC for ABORT next instruction address */ sub sp, sp, #(4*16) /* Need to manually update SP(abort) */ stmfd sp!, {r0,r1} /* Save ABORTed Next Instr Pointer (in R0) and previous mode's CPSR to stack */ and r2, r1, #CPSR_MODE /* Get previous mode */ teq r2, #MODE_USR beq _exit_abort_handler /* Can't switch back if we're in User mode! */ _store_prev_mode_banked_regs: /* FIXME: We don't handle FIQ properly! */ orr r2, #(CPSR_FIQ | CPSR_IRQ) /* Disable Interrupts */ msr cpsr_c, r2 /* Switch to previous mode */ stmfd r3!, {sp, lr} /* Store Previous Mode's LR (R14), SP (R13) via R3 */ msr cpsr_c, #(MODE_ABT | CPSR_FIQ | CPSR_IRQ) /* Revert to ABORT Mode */ _exit_abort_handler: ldr sp, =__abort_stack__ /* Reinitialize stack pointer each time an Abort happens */ bic sp, sp, #7 mov r0, r4 /* Copy Display Abort Type Enum to R0 */ bl dbg__display_abort_info /* Display Abort Type to LCD */ mov r0, r5 /* Copy Debugger Abort Type Enum to R0 */ b dbg__abort_exception_handler /* Invoke Debugger */ nxt-firmware-1.29.7/armdebug/Debugger/debug_comm.S000066400000000000000000000572411466344546000220210ustar00rootroot00000000000000/** @file debug_comm.S * @brief GDB Server communications support routines * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #define __ASSEMBLY__ #include "debug_macros.h" #include "debug_stub.h" #include "debug_internals.h" .extern dbg__sendCommMsg /* Hexutils function references */ .extern hex2char .extern char2hex .extern byte2ascii .extern halfword2ascii_be .extern halfword2ascii_le .extern word2ascii_be .extern word2ascii_le .extern ascii2hex_varlen_be .extern ascii2byte .extern ascii2halfword_be .extern ascii2halfword_le .extern ascii2word_be .extern ascii2word_le .bss .align 4 .global debug_InCommBuf .global debug_OutCommBuf debug_InCommBuf: .space USB_BUFSIZE,0 debug_OutCommBuf: .space USB_BUFSIZE,0 debug_msgRxBufPtr: .word 0x0 debug_msgTxBufPtr: .word 0x0 debug_msgRxBuf_AppendPtr: .word 0x0 debug_msgTxBuf_AppendPtr: .word 0x0 .equ RXAPPENDPTR_OFFSET, (debug_msgRxBuf_AppendPtr - debug_msgRxBufPtr) .equ TXAPPENDPTR_OFFSET, (debug_msgTxBuf_AppendPtr - debug_msgTxBufPtr) debug_segmentRxNum: /* Current Rx Segment Number */ .word 0x0 /* Comm Channel and NXT Received Message Length is now common to both NxOS and NXT Firmware */ debug_nxtMsgLength: .word 0x0 .global debug_nxtCommChannel debug_nxtCommChannel: .word 0x0 .global debug_nxtCommOverrun debug_nxtCommOverrun: .word 0x0 .equ NXTCOMMCHANNEL_OFFSET, (debug_nxtCommChannel - debug_nxtMsgLength) .equ NXTCOMMOVERRUN_OFFSET, (debug_nxtCommOverrun - debug_nxtMsgLength) .data .align 4 nxt_commcmd_header: .byte NXT_GDBMSG_TELEGRAMTYPE, 0x00, 0x00 /* padded to 3 bytes */ .code 32 .text .align 4 /* Debugger Communications Routines * It does not make sense to pass information from the Debugger Module to the Comm. link one character * at a time, especially if we're not using a native serial interface (e.g., EIA-232). Consequently * a Message interface has been defined. This can still call getChar() and putChar() subroutines * if so desired, but it'll be a purely internal matter. * * Message Format * Since we need to use EP1&2 (Bulk channels) to communicate with the PC Host, the messages should * follow the NXT Direct Commands message structure (this will allow for interoperability with NXT Firmware * in addition to NxOS). The maximum length of any USB communications via the Bulk channel is 64 bytes. * There is a one byte Telegram Type field which identifies the type of telegram, followed by the * Telegram header and actual message. * * The LEGO Mindstorms Communications Protocol Direct Commands GDB Message format (including all headers) * is as follows: * * GDB Command * =========== * Byte 0: Telegram Type Field (0x8d Direct Command, No response required) | NXT Msg Header * Byte 1: Segment No (1-255, 0: Last Segment; limit is MSG_NUMSEGMENTS) | * Byte 2: Telegram Size (Len of USB Buffer - 3, max is MSG_SEGMENTSIZE) | * Byte 3-N: Message data | GDB Command * * The GDB Command (of size M) has the following format: * Offset 0: '+'/'-' Command Received Status (Optional) * Offset 1/0: '$' * Offset 2/1: GDB Command char * Offset 3 - (M-4): Command packet info * Offset M-3: '#' * Offset M-2: MSB of Checksum * Offset M-1: LSB of Checksum * * To be safe, we assume that the Command Received Status is always sent by the GDB server. Therefore, * The maximum size of a GDB Command packet is MSGBUF_SIZE - 5 ('+'/'-', '$', '#', 2 byte checksum) * * GDB Response * ============ * Byte 0: Telegram Type Field (0x8d Direct Command, No response required) | NXT Msg Header * Byte 1: Segment No (1-255, 0: Last Segment; limit is MSG_NUMSEGMENTS) | * Byte 2: Telegram Size (Len of USB Buffer - 3, max is MSG_SEGMENTSIZE) | * Byte 3-N: Message data | GDB Response * * The GDB Retransmission Request has the following format: * Offset 0: '-' Command Received Status * * The GDB Response (of size M) has the following format: * Offset 0: '+' Command Received Status * Offset 1: '$' * Offset 2 - (M-4): Response packet info * Offset M-3: '#' * Offset M-2: MSB of Checksum * Offset M-1: LSB of Checksum * * The maximum size of a GDB Response packet is MSGBUF_SIZE - 5 ('+', '$', '#', 2 byte checksum) * * Note: The Telegram Size is the actual size of the Message Data portion * (i.e., excludes the three header bytes, includes the GDB Command/Response Packet checksum bytes * in the last segment) */ /* dbg__comm_init * Initialize communications channel. * On Entry: * R0: MSG Rx Buf Pointer * R1: MSG Tx Buf Pointer */ dbg_interwork dbg__comm_init stmfd sp!, {lr} ldr r2, =debug_msgRxBufPtr stmia r2!, {r0, r1} /* debug_msgRxBufPtr and debug_msgTxBufPtr */ stmia r2!, {r0, r1} /* debug_msgRxBuf_AppendPtr and debug_msgTxBuf_AppendPtr */ bl _dbg__comm_readbuf_reset ldr r1, =debug_nxtMsgLength mov r0, #0 str r0, [r1, #NXTCOMMCHANNEL_OFFSET] /* Clear NXT Channel on INIT */ ldmfd sp!, {pc} _dbg__comm_readbuf_reset: ldr r1, =debug_nxtMsgLength mov r0, #0 str r0, [r1] /* Clear Received Comm Message Length */ bx lr /* dbg__copyNxtDebugMsg * Copy NXT Debug Message to our own Buffers, indicate Msg Received status. * Note: This routine is now used by both NXT Firmware and NxOS * On Entry: * R0: NXT Input Buf Pointer * R1: NXT Communications Channel Enum (CmdBit) * R2: NXT Raw Message Length * On Exit: * R0-R3: Destroyed */ dbg_interwork dbg__copyNxtDebugMsg ldr r3, =debug_nxtMsgLength str r1, [r3, #NXTCOMMCHANNEL_OFFSET] /* save Communications Channel first */ ldr r1, [r3] /* Check if there's an unread message in the buffer */ cmp r1, #0 beq cont_dbg__copyNxtDebugMsg /* No unread message, so continue */ exit_dbg__NxtDebugMsgOverrun: ldr r1, [r3, #NXTCOMMOVERRUN_OFFSET] add r1, r1, #1 str r1, [r3, #NXTCOMMOVERRUN_OFFSET] /* update message overrun stats */ b exit_dbg__copyNxtDebugMsg cont_dbg__copyNxtDebugMsg: str r2, [r3] ldr r1, =debug_InCommBuf _dbg_memcpy r1, r0, r2, r3 /* r3: scratch register */ exit_dbg__copyNxtDebugMsg: bx lr /* _dbg_reset_msgTxBuf_AppendPtr * Internal variable to reset pointers. * On Exit: * R0: debug_msgTxBuf_AppendPtr * R1: destroyed */ _dbg_reset_msgTxBuf_AppendPtr: ldr r1, =debug_msgTxBufPtr /* Should not be modified */ ldr r0, [r1] str r0, [r1, #TXAPPENDPTR_OFFSET] mov pc, lr /* _dbg__commHasMsg * Internal Segment Reassembly Routine. * On exit: * r0: !0: (Availale Telegram Message Size), 0: no incoming message/segment * r1: message segment number */ _dbg__commHasMsg: stmfd sp!, {lr} ldr r0, =debug_nxtMsgLength ldr r0, [r0] /* R0 contains the Comm Buffer Size, including the NXT Direct Command Header */ ldr r2, =debug_InCommBuf ldrb r1, [r2, #NXT_MSG_TELEGRAMTYPE_OFFSET] cmp r1, #NXT_GDBMSG_TELEGRAMTYPE bne invalid_CommMsg /* Invalid telegram type, ignore */ ldrb r1, [r2, #NXT_MSG_TELEGRAMSIZE_OFFSET] sub r0, r0, r1 /* Comm Buffer Size - Telegram Size = 3 (header size) */ cmp r0, #NXT_GDBMSG_START /* Start offset is equal to header size */ bne invalid_CommMsg /* Invalid Message Length, ignore */ mov r0, r1 /* Telegram Message Size */ ldrb r1, [r2, #NXT_MSG_SEGNUM_OFFSET] b _exit_dbg__commHasMsg invalid_CommMsg: bl _dbg__comm_readbuf_reset /* Next Comm telegram transaction */ mov r0, #0 _exit_dbg__commHasMsg: ldmfd sp!, {pc} /* _copy_msg_from_commbuf * Internal Comm buffer copy routine, handles segment reassembly. * On entry: * r0: number of bytes to copy * r1: segment number * On exit: * r0: cummulative message length * r1: segment number * r2, r3: Destroyed */ _copy_msg_from_commbuf: stmfd sp!, {r1,r4,r5,r6,lr} movs r4, r0 beq _exit_copy_msg_from_commbuf ldr r6, =debug_msgRxBufPtr /* Address of Pointers */ ldr r5, [r6] /* Rx buffer Start Address */ ldr r2, [r6, #RXAPPENDPTR_OFFSET] /* Append Pointer */ sub r3, r2, r5 /* r3: current length of message */ add r3, r3, r4 /* new cummulative length of message */ cmp r3, #MSGBUF_SIZE movhi r4, #0 /* Buffer overflow! */ strhi r5, [r6, #RXAPPENDPTR_OFFSET] /* Reset AppendPtr to beginning of Rx Buffer */ bhi _exit_copy_msg_from_commbuf ldr r3, =debug_InCommBuf add r3, r3, #NXT_GDBMSG_START _dbg_memcpy r2, r3, r4, r0 /* r2 updated to point to next empty char slot in Rx buffer */ sub r4, r2, r5 /* r4: cummulative length of message */ /* Update debug_msgRxBuf_AppendPtr */ teq r1, #0 /* Check if this is last segment (segment 0) */ streq r5, [r6, #RXAPPENDPTR_OFFSET] /* Reset AppendPtr to beginning of Rx Buffer if so */ strne r2, [r6, #RXAPPENDPTR_OFFSET] /* Otherwise, update Append Pointer to receive next segment */ _exit_copy_msg_from_commbuf: bl _dbg__comm_readbuf_reset /* Next Comm telegram transaction */ mov r0, r4 /* Return cummulative message length in R0 */ ldmfd sp!, {r1,r4,r5,r6,pc} /* Return segment number in R1 */ /* _msgbuf_checksum * Internal routine to calculate checksum character buffer. * On entry: * r0: pointer to character buffer to checksum (assume ASCIIZ terminated) * On exit: * r0: pointer to character buffer after ASCIIZ * r1: checksum (8-bit binary) * r2: message length * r3: destroyed */ _msgbuf_checksum: mov r1, #0 /* clear checksum */ mov r2, #0 /* clear length */ 1: ldrb r3, [r0], #1 /* Iterate through buffer */ add r1, r1, r3 /* cummulative sum of char */ teq r3, #0 addne r2, r2, #1 /* increment message length */ bne 1b /* until ASCIIZ found */ and r1, #BYTE0 /* Modulo 256 */ mov pc, lr /* dbg__getDebugMsg * Retrieve pending Debugger Message if available (Non-Blocking). * On entry: * No parameters (assume pointers were initialized previously using dbg__comm_init) * On exit: * r0: >0 = Valid GDB Message Length (incl '$', excluding '#' and checksum), * 0 = no valid message (yet), -1 = error * r1: GDB Message Buffer Pointer (incl '$', excluding '#' and checksum) * r2, r3: Destroyed * Note: If GDB Message were returned, it is ASCIIZ terminated, does not include '#' and checksum */ dbg_interwork dbg__getDebugMsg stmfd sp!, {r4,r5,lr} bl _dbg__commHasMsg /* r0: message length, r1: segment number */ teq r0, #0 beq exit_dbg__getDebugMsg /* no new message, exit with R0 = 0 */ ldr r4, =debug_segmentRxNum ldr r2, [r4] /* Get current Segment Number */ add r2, r2, #1 /* Expected Segment Number for comparison */ teq r1, #0 streq r1, [r4] /* Update current Segment Number with 0 since it is the last segment */ beq _hasMsg2Copy cmp r1, #MSG_NUMSEGMENTS /* Segment Number < MSG_NUMSEGMENTS? */ bhs _invalid_segment teq r1, r2 /* Valid Segment Number, check against Expected Segment Number */ beq _hasMsg2Copy /* Segment Number matches Expected Segment Number, update buffers */ _invalid_segment: bl _dbg__comm_readbuf_reset /* Invalid, Next Comm telegram transaction */ mov r0, #0 /* Reset Segment Number */ str r0, [r4] /* Update current Segment Number with 0 to prepare for new message */ b exit_dbg__getMsgError /* Exit with error */ _hasMsg2Copy: str r1, [r4] /* Update current Segment Number */ bl _copy_msg_from_commbuf /* r0: cummulative message length, r1: segment number */ teq r1, #0 movne r0, #0 /* Incomplete message, ignore for now */ bne exit_dbg__getDebugMsg /* Message not complete yet, exit */ /* Check for valid GDB message */ mov r4, r0 /* keep message length in R4, assume to be within MSGBUF_SIZE */ ldr r5, =debug_msgRxBufPtr ldr r5, [r5] /* Rx buffer Start Address */ /* Need to account for Packet Acknowledgement */ 1: ldrb r0, [r5] teq r0, #MSGBUF_CTRLC /* Look for Ctrl-C */ moveq r0, r4 /* If found, set R0 to current message length */ beq exit_dbg__getDebugMsg /* and return */ teq r0, #MSGBUF_NAKCHAR /* Look for '-' */ beq exit_dbg__getMsgError /* Error from Host, Retransmit previous message */ teq r0, #MSGBUF_ACKCHAR /* Look for '+' */ addeq r5, r5, #1 /* Adjust Buffer Start Pointer (excl '+') */ subeq r4, r4, #1 /* Adjust Message Length */ beq 1b /* Skip all Packet Acknowledgements */ /* Note: Here we assume that we won't get a single ACK '+' or NAK '-' character message. * If we do, it'll be flagged as an error */ subs r2, r4, #MSGBUF_CHKSUMOFFSET /* Look for '#': Message Length - 3 = '#' offset */ blt exit_dbg__getMsgError /* Message Length is too short, exit with error */ ldrb r0, [r5, r2] teq r0, #MSGBUF_CHKSUMCHAR bne exit_dbg__getMsgError /* No checksum char '#', exit with error */ mov r1, #0 strb r1, [r5, r2] /* Zero out '#' char for checksum calc later */ #ifdef CHECK_GDBSTARTCHAR /* Checked in dbg__bkpt_waitCMD */ ldrb r0, [r5] teq r0, #MSGBUF_STARTCHAR /* Look for '$' */ bne exit_dbg__getMsgError /* No start char '$', exit with error */ #endif add r0, r5, #1 /* Checksum packet data (excl '$') */ bl _msgbuf_checksum /* R2: length (excl '$'), R1: calculated checksum, R0: pointer to checksum in receive buffer */ mov r3, r1 /* Keep calculated checksum in R3 (R1 destroyed by ascii2byte) */ bl ascii2byte /* R0: received checksum, R1: address of next buffer location */ teq r0, r3 /* Compare calculated checksum in R3 against received checksum in R0 */ bne exit_dbg__getMsgError /* Checksums do not match, exit with error */ subeq r0, r4, #MSGBUF_CHKSUMOFFSET /* Update message length (incl '$') as return parameter */ add r2, r2, #1 /* expected message length (from _msgbuf_checksum) */ teq r0, r2 beq exit_dbg__getDebugMsg /* Valid length, return */ exit_dbg__getMsgError: /* We must first clear the existing message checksum */ ldr r1, =debug_msgTxBufPtr /* R5: data structure base pointer */ ldr r1, [r1] /* Tx buffer Start Address */ 1: ldrb r0, [r1], #1 teq r0, #MSGBUF_CHKSUMCHAR bne 1b mov r0, #0 /* ASCIIZ */ strb r0, [r1, #-1] /* Pointer R1 is now one past the MSGBUF_CHKSUMCHAR */ bl dbg__putDebugMsg /* Retransmit message */ mov r0, #0 /* Flag no message received */ #if 0 mov r0, #MSGBUF_MSGERROR #endif exit_dbg__getDebugMsg: mov r1, r5 /* Return GDB Message Buffer Pointer in R1 */ ldmfd sp!, {r4,r5,pc} /* _copy_msg_to_commbuf * Internal Comm buffer copy routine, handles segment fragmentation. * On entry: * r0: number of bytes to copy * r1: segment number * On exit: * r0: cummulative message length * r1: segment number * r2, r3: Destroyed */ _copy_msg_to_commbuf: stmfd sp!, {r1,r4,r5,r6,lr} ldr r6, =debug_msgTxBufPtr /* Address of Pointers */ ldr r5, [r6, #TXAPPENDPTR_OFFSET] /* Retrieve Tx Append Pointer */ movs r4, r0 beq _exit_copy_msg_to_commbuf #ifdef CHECK_TXLEN add r0, r4, #NXT_GDBMSG_START /* offset = header size */ cmp r0, #USB_BUFSIZE bhi _exit_copy_msg_to_commbuf /* We let calling routine detect problem (segment number will increment) */ #endif /* Fill in Comm Message Header */ ldr r3, =debug_OutCommBuf mov r2, #NXT_GDBMSG_TELEGRAMTYPE strb r2, [r3], #1 /* Telegram type */ strb r1, [r3], #1 /* Segment Number */ strb r0, [r3], #1 /* Message Length */ mov r2, r5 /* Copy to R2 for updating */ mov r1, r4 /* actual GDB message fragment length (exclude Comm header) */ _dbg_memcpy r3, r2, r1, r0 /* This copies over the message fragment, r3, r2 updated */ mov r5, r2 /* Updated Tx Append Pointer, keep in R5 for now */ add r0, r4, #NXT_GDBMSG_START /* Total Comm Buffer Size for Tx (NXT_GDBMSG_START offset = header size) */ bl dbg__sendCommMsg /* Common interface routine to commnuncations drivers */ cmp r0, #TRUE ldrne r5, [r6, #TXAPPENDPTR_OFFSET] /* Tx failed, Retrieve Original Tx Append Pointer */ streq r5, [r6, #TXAPPENDPTR_OFFSET] /* Tx succeeded, Update Tx Append Pointer to new position */ _exit_copy_msg_to_commbuf: ldr r6, [r6] /* Retrieve Tx Buffer Start Address */ sub r0, r5, r6 /* Return calculated cummulative message length (R0) */ ldmfd sp!, {r1,r4,r5,r6,pc} /* Return segment number in R1 */ /* dbg__putDebugMsg * Sends Debugger Message from calling routine after appending checksum (Blocking) . * On entry: * No parameters (assume pointers were initialized previously using dbg__comm_init) * On exit: * r0: status (0: success, -1: error) * Note: GDB Message to be sent must be ASCIIZ terminated, does not include '#' and checksum * Response packets start with '+' followed by '$' (2 bytes prefix) */ dbg_interwork dbg__putDebugMsg stmfd sp!, {r4,r5,lr} /* Perform Checksum Calculation */ ldr r5, =debug_msgTxBufPtr /* R5: data structure base pointer */ ldr r4, [r5] /* Tx buffer Start Address */ str r4, [r5, #TXAPPENDPTR_OFFSET] /* Reset Tx buffer Append Pointer */ add r0, r4, #2 /* skip '+' and '$' */ bl _msgbuf_checksum /* R2: length (excl '+' and '$'), R1: calculated checksum, R0: pointer to checksum in tx buffer */ #ifdef CHECK_TXLEN add r2, r2, #2 /* r2: returned length from _msgbuf_checksum, added with prefix length */ sub r3, r0, r4 /* r3: calculated length from pointers (incl. prefix length) */ teq r2, r3 bne exit_dbg__putMsgError #endif mov r3, #MSGBUF_CHKSUMCHAR strb r3, [r0, #-1] /* Insert '#' */ bl byte2ascii /* On return, R0 points to location after checksum bytes, R1 is original pointer to checksum */ sub r4, r0, r4 /* R4 = Calculated total message length (incl '+' and '$', '#' and checksum bytes */ cmp r4, #MSG_SEGMENTSIZE /* If total message length > MSG_SEGMENTSIZE */ mov r1, #0 /* Initialize Segment Number = 0 (last segment) first */ mov r0, #0 /* Initial cummulative message length */ mov r5, #0 /* Previous cummulative message length */ /* We assume unsigned message lengths, so the arithmetic MUST NOT result in negative values */ _cont_putMsg: cmp r4, r0 movls r0, #0 /* R0: Exit status (success) */ bls exit_dbg__putDebugMsg /* If Total message length (r4) <= Cummulative message length (r0), we're done */ add r2, r0, #MSG_SEGMENTSIZE /* R2: calculate new Max cummulative message length */ cmp r4, r2 /* Check total message length (R4) against new Max cummulative message length (R2) */ subls r0, r4, r0 /* if total message length (R4) <= new Max cummulative message length (R2), send remainder */ movls r1, #0 /* Flag as last segment (Segment Number = 0) */ movhi r0, #MSG_SEGMENTSIZE /* else send MSG_SEGMENTSIZE bytes */ addhi r1, r1, #1 /* Increment Segment Number */ cmp r1, #MSG_NUMSEGMENTS bhs exit_dbg__putMsgError /* If Segment Number >= MSG_NUMSEGMENTS, flag error */ bl _copy_msg_to_commbuf /* R0: cummulative message length, R1: segment number */ teq r5, r0 /* Check if we managed to transmit the previous message */ beq exit_dbg__putMsgError /* No, flag error */ movne r5, r0 /* Update previous cummulative message length */ b _cont_putMsg exit_dbg__putMsgError: mov r0, #MSGBUF_MSGERROR exit_dbg__putDebugMsg: ldmfd sp!, {r4,r5,pc} /* dbg__sendAckOrNak * Send Ack (for successful receipt of message) * or Nak (for Retransmission due to received message Checksum error) (Blocking) . * On entry: * No parameters (assume pointers were initialized previously using dbg__comm_init) * On exit: * r0: status (0: success, -1: error) * r1: destroyed * Note: An Ack Or Nak is indicated by '+' or '-', which is prepended with the Comm header and sent (without checksum) * Sending Ack is only done for Continue and Step commands, where GDB does not expect any replies. */ dbg_interwork dbg__sendAckOrNak stmfd sp!, {lr} ldr r1, =debug_msgTxBufPtr /* R2: data structure base pointer */ ldr r0, [r1] /* Tx buffer Start Address */ str r0, [r1, #TXAPPENDPTR_OFFSET] /* Reset Tx buffer Append Pointer */ mov r1, #0 /* Initialize Segment Number = 0 (last segment) */ mov r0, #1 /* Retransmission message length = 1 */ bl _copy_msg_to_commbuf /* R0: cummulative message length, R1: segment number */ cmp r0, #1 /* Check if we managed to transmit the previous message */ moveq r0, #0 /* R0: Exit status (success) */ movne r0, #MSGBUF_MSGERROR /* R0: Exit status (error) */ ldmfd sp!, {pc} nxt-firmware-1.29.7/armdebug/Debugger/debug_hexutils.S000066400000000000000000000377701466344546000227400ustar00rootroot00000000000000/** @file debug_hexutils.S * @brief GDB hexadecimal conversion utility routines * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #define __ASSEMBLY__ #include "debug_internals.h" .data .align 4 hex2char_lut: /* .ascii "0123456789ABCDEF" */ /* Need to use lowercase hex chars to avoid confusion with GDB Error (E NN) response */ .ascii "0123456789abcdef" /* Macros */ /* _hex2char_lut * Internal routine to intialize the LUT address pointer */ .macro _hex2char_lut addrptr ldr \addrptr, =hex2char_lut .endm /* _hex2char_cont * Internal routine that assumes that the LUT has been loaded. * This macro accepts a byte sized hex value as a parameter register(7:0) and returns the * ASCII equivalent in in the same register(7:0) * The second parameter is the LUT address pointer register to use (assumed to be initialized) * WARNING: Assumes that the value in register is sanity checked before invoking macro */ .macro _hex2char_cont reg, addrptr ldrb \reg, [\addrptr, \reg] .endm /* _hex2char * This macro accepts a byte sized hex value as a parameter register(7:0) and returns the * ASCII equivalent in in the same register(7:0) * The second parameter is the LUT address pointer register to use (register content is destroyed) * WARNING: Assumes that the value in register is sanity checked before invoking macro */ .macro _hex2char reg, addrptr _hex2char_lut \addrptr _hex2char_cont \reg, \addrptr .endm /* _char2hex * This macro accepts an ASCII char as a parameter register(7:0) and returns the * equivalent byte sized hex value in in the same register(7:0) * WARNING: Assumes that the char in register is a valid hex char before invoking macro */ .macro _char2hex reg cmp \reg, #'A' /* If Alpha */ bichs \reg, \reg, #ASCII_LOWER2UPPER_MASK /* Convert to Uppercase */ subhs \reg, \reg, #7 /* Adjustment to allow for subtraction with 0x30 */ sub \reg, \reg, #0x30 /* get final hex value */ .endm .code 32 .text .align 4 /* Utility Routines * GDB requires command parameters to be specified as Big Endian values. * However, the read/write register command expect the register contents to be specified in target byte order. * Hence we need both versions of multibyte conversion routines for word sized values. */ /* hex2char * This routine accepts a byte sized hex value in R0(7:0) and returns the * ASCII equivalent in R0(7:0) */ .global hex2char hex2char: stmfd sp!, {r1,lr} and r0, #NIBBLE0 /* make sure that input is sane */ _hex2char r0, r1 ldmfd sp!, {r1,pc} /* char2hex * This routine accepts an ASCII character in R0(7:0) and returns the * equivalent byte sized hex value in R0(7:0). * It accepts lowercase and uppercase ASCII Hex char inputs. * Invalid inputs return -1 as the value * On entry: * R0: ASCII character * On exit: * R0: Hex value */ .global char2hex char2hex: and r0, r0, #BYTE0 /* make sure that input is sane */ cmp r0, #'0' blo char2hex_error cmp r0, #'9' bls perform_char2hex cmp r0, #'A' blo char2hex_error cmp r0, #'F' bls perform_char2hex cmp r0, #'a' blo char2hex_error cmp r0, #'f' bhi char2hex_error /* Validated Hex Char */ perform_char2hex: _char2hex r0 /* Return hex value in R0 */ bx lr char2hex_error: mov r0, #-1 /* Set Return value to Error value */ bx lr /* byte2ascii_cont * (Shared routine, does not perform sanity checks) * On entry: * R0: ASCII buffer pointer * R1[7:0]: byte value * On exit: * R0: Address of next empty char slot in buffer * R1: Destroyed * * This routine accepts an ASCII buffer pointer in R0 and a byte value in R1, * and stores the ASCII equivalent byte value in the buffer pointed to by R0. * Note: On return, R0 points to next empty char slot in buffer */ byte2ascii_cont: stmfd sp!, {r2,r3,r4, lr} lsl r2, r1, #24 /* Keep copy of input byte value R1[7:0], shifted to MSB R2[31:24] */ mov r4, #2 /* Loop counter */ _hex2char_lut r3 /* initialize LUT pointer */ 1: ror r2, r2, #28 /* Rotate MSNibble R2[31:28] into LSNibble position R2[3:0] */ and r1, r2, #NIBBLE0 /* Mask out everything else, store Nibble in R1 */ _hex2char_cont r1, r3 /* Convert nibble to ASCII char */ strb r1, [r0], #1 subs r4, r4, #1 /* decrement loop counter */ bne 1b ldmfd sp!, {r2,r3,r4, pc} /* byte2ascii * On entry: * R0: ASCII buffer pointer * R1[7:0]: Byte value * On exit: * R0: Address of next empty char slot in buffer * R1: Original Address of Buffer * * This routine accepts an ASCII buffer pointer in R0 and a byte value in R1, * and stores the ASCII equivalent byte value in the buffer pointed to by R0. * Note: On return, R0 points to the next empty char slot in buffer */ .global byte2ascii byte2ascii: stmfd sp!, {r0, lr} /* Keep ASCII buffer pointer */ and r1, #BYTE0 /* sanitize input */ bl byte2ascii_cont ldmfd sp!, {r1, pc} /* return original string pointer in R1 */ /* halfword2ascii_be * Big Endian version of halfword2ascii conversion routine * On entry: * R0: ASCII buffer pointer * R1[15:0]: Halfword value * On exit: * R0: Address of next empty char slot in buffer * R1: Original Address of Buffer * * This routine accepts an ASCII buffer pointer in R0 and a halfword value in R1, * and stores the ASCII equivalent halfword value in the buffer pointed to by R0. * Note: On return, R0 points to the next empty char slot in buffer */ .global halfword2ascii_be halfword2ascii_be: stmfd sp!, {r0,r2,r3, lr} /* Keep ASCII buffer pointer */ mov r3, #2 /* Loop Counter */ mov r2, r1, lsl #16 /* copy of input halfword value R1[15:0], shifted to MSH R2[31:16] */ b _conv_byte2ascii_be /* goto Byte conversion loop */ /* halfword2ascii_le * Little Endian version of halfword2ascii conversion routine * On entry: * R0: ASCII buffer pointer * R1[15:0]: Halfword value * On exit: * R0: Address of next empty char slot in buffer * R1: Original Address of Buffer * * This routine accepts an ASCII buffer pointer in R0 and a halfword value in R1, * and stores the ASCII equivalent halfword value in the buffer pointed to by R0. * Note: On return, R0 points to the next empty char slot in buffer */ .global halfword2ascii_le halfword2ascii_le: stmfd sp!, {r0,r2,r3, lr} /* Keep ASCII buffer pointer */ mov r3, #2 /* Loop Counter */ b _conv_byte2ascii_le /* goto Byte conversion loop */ /* word2ascii_be * Big Endian version of word2ascii conversion routine * On entry: * R0: ASCII buffer pointer * R1[31:0]: Word value * On exit: * R0: Address of next empty char slot in buffer * R1: Original Address of Buffer * * This routine accepts an ASCII buffer pointer in R0 and a word value in R1, * and stores the ASCII equivalent word value in the buffer pointed to by R0. * Note: On return, R0 points to the next empty char slot in buffer */ .global word2ascii_be word2ascii_be: stmfd sp!, {r0,r2,r3, lr} /* Keep ASCII buffer pointer */ mov r2, r1 /* copy of input word value R1[31:0] */ mov r3, #4 /* Loop Counter */ /* Fall through to byte coversion loop */ /* Big Endian Multibyte Convert: Rotate then convert */ _conv_byte2ascii_be: ror r2, r2, #24 /* Rotate MSB R2[31:24] into LSB position R2[7:0] */ and r1, r2, #BYTE0 /* Copy byte value in R2[7:0] into R1 */ bl byte2ascii_cont /* R0: next ASCII buffer location pointer, R1: destroyed */ subs r3, r3, #1 bne _conv_byte2ascii_be ldmfd sp!, {r1,r2,r3, pc} /* word2ascii_le * Little Endian version of word2ascii conversion routine * On entry: * R0: ASCII buffer pointer * R1[31:0]: Word value * On exit: * R0: Address of next empty char slot in buffer * R1: Original Address of Buffer * * This routine accepts an ASCII buffer pointer in R0 and a word value in R1, * and stores the ASCII equivalent word value in the buffer pointed to by R0. * Note: On return, R0 points to the next empty char slot in buffer */ .global word2ascii_le word2ascii_le: stmfd sp!, {r0,r2,r3, lr} /* Keep ASCII buffer pointer */ mov r2, r1 /* copy of input word value R1[31:0] */ mov r3, #4 /* Loop Counter */ /* Fall through to byte coversion loop */ /* Little Endian Multibyte Convert: Convert then rotate */ _conv_byte2ascii_le: and r1, r2, #BYTE0 /* Copy byte value in R2[7:0] into R1 */ bl byte2ascii_cont /* R0: next ASCII buffer location pointer, R1: destroyed */ ror r2, r2, #8 /* Rotate LSB+1 R2[15:8] into LSB position R2[7:0] */ subs r3, r3, #1 bne _conv_byte2ascii_le ldmfd sp!, {r1,r2,r3, pc} /* ascii2hex_varlen_be * Big Endian version of ascii2hex_varlen conversion routine * (There is no Little Endian Version) * On entry: * R0: ASCII buffer pointer * On exit: * R0: Hex value * R1: Address of next char slot in buffer * * This routine accepts an ASCII buffer pointer in R0, * and returns the hex value in R0 for up to 8 Hex characters. * Note: On return, R1 points to the ASCII buffer location after the hex value chars. */ .global ascii2hex_varlen_be ascii2hex_varlen_be: stmfd sp!, {r2,r3, lr} mov r3, #CMD_REG_REGPARAMLEN /* Set max count to 8 (Max Register size) */ mov r1, r0 /* Use R1 as ASCII buffer pointer */ mov r2, #0 /* Initialize Cummulative Results */ 2: ldrb r0, [r1] /* Load ASCII char for Hex Value */ bl char2hex /* on return, hex value in R0, -1 for error */ cmp r0, #-1 beq _exit_ascii2hex_varlen orr r2, r0, r2, lsl #4 /* combined byte value */ subs r3, r3, #1 /* Decrement Counter */ add r1, r1, #1 /* Go to next char slot */ bne 2b _exit_ascii2hex_varlen: mov r0, r2 /* Return results in R0 */ ldmfd sp!, {r2,r3, pc} /* ascii2byte * On entry: * R0: ASCII buffer pointer * On exit: * R0[7:0]: Byte value * R1: Address of next char slot in buffer * * This routine accepts an ASCII buffer pointer in R0, * and returns the byte value in R0[7:0]. * Note: On return, R1 points to the ASCII buffer location after the current 2 chars. * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, * otherwise it will return invalid results. */ .global ascii2byte ascii2byte: stmfd sp!, {r2, lr} mov r1, r0 /* Use R1 as ASCII buffer pointer */ ldrb r0, [r1], #1 /* Load ASCII char for MSN */ bl char2hex /* on return, hex value in R0, -1 for error (ignored) */ mov r2, r0, lsl #4 /* Intermediate Results register */ ldrb r0, [r1], #1 /* Load ASCII char for LSN */ bl char2hex /* on return, hex value in R0, -1 for error (ignored) */ orr r0, r2, r0 /* combined byte value */ ldmfd sp!, {r2, pc} /* ascii2halfword_be * Big Endian version of ascii2halfword conversion routine * On entry: * R0: ASCII buffer pointer * On exit: * R0[15:0]: Halfword value * R1: Address of next char slot in buffer * * This routine accepts an ASCII buffer pointer in R0, * and returns the Halfword value in R0[15:0]. * Note: On return, R1 points to the ASCII buffer location after the current 4 chars. * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, * otherwise it will return invalid results. */ .global ascii2halfword_be ascii2halfword_be: stmfd sp!, {r2,r3, lr} mov r3, #2 /* Loop counter */ b _conv_ascii2byte_be /* ascii2halfword_le * Little Endian version of ascii2halfword conversion routine * On entry: * R0: ASCII buffer pointer * On exit: * R0[15:0]: Halfword value * R1: Address of next char slot in buffer * * This routine accepts an ASCII buffer pointer in R0, * and returns the Halfword value in R0[15:0]. * Note: On return, R1 points to the ASCII buffer location after the current 4 chars. * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, * otherwise it will return invalid results. */ .global ascii2halfword_le ascii2halfword_le: stmfd sp!, {r2,r3, lr} mov r3, #2 /* Loop counter */ b _conv_ascii2byte_le /* ascii2word_be * Big Endian version of ascii2word conversion routine * On entry: * R0: ASCII buffer pointer * On exit: * R0[31:0]: Word value * R1: Address of next char slot in buffer * * This routine accepts an ASCII buffer pointer in R0, * and returns the word value in R0[31:0]. * Note: On return, R1 points to the ASCII buffer location after the current 8 chars. * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, * otherwise it will return invalid results. */ .global ascii2word_be ascii2word_be: stmfd sp!, {r2,r3, lr} mov r3, #4 /* Loop counter */ /* Fall through to byte coversion loop */ _conv_ascii2byte_be: teq r0, #0 beq _exit_conv_ascii2byte_be /* exit if NULL pointer in R0 */ mov r2, #0 /* Initialize Cummulative value */ 2: bl ascii2byte orr r2, r0, r2, lsl #8 /* Merge current byte with cummulative value */ mov r0, r1 /* Copy next char pointer to R0 for next byte */ subs r3, r3, #1 bne 2b mov r0, r2 /* Copy it to R0 as return value */ _exit_conv_ascii2byte_be: ldmfd sp!, {r2,r3, pc} /* return hex value in R0 */ /* ascii2word_le * Litle Endian version of ascii2word conversion routine * On entry: * R0: ASCII buffer pointer * On exit: * R0[31:0]: Word value * R1: Address of next char slot in buffer * * This routine accepts an ASCII buffer pointer in R0, * and returns the word value in R0[31:0]. * Note: On return, R1 points to the ASCII buffer location after the current 8 chars. * WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars, * otherwise it will return invalid results. */ .global ascii2word_le ascii2word_le: stmfd sp!, {r2,r3, lr} mov r3, #4 /* Loop counter */ /* Fall through to byte coversion loop */ _conv_ascii2byte_le: teq r0, #0 beq _exit_conv_ascii2byte_le /* exit if NULL pointer in R0 */ push {r3} /* Need to keep couter for final value adjustment */ mov r2, #0 /* Initialize Cummulative value */ 2: bl ascii2byte orr r2, r0, r2, ror #8 /* Merge current byte with cummulative value */ mov r0, r1 /* Copy next char pointer to R0 for next byte */ subs r3, r3, #1 bne 2b /* Cummulative value done, need to rotate it into the correct position for return value */ pop {r3} /* retrieve counter */ rsb r3, r3, #5 /* 5 - count */ lsl r3, r3, #3 /* [(5-count) x 8] bits to rotate */ mov r0, r2, ror r3 /* Copy it to R0 as return value */ _exit_conv_ascii2byte_le: ldmfd sp!, {r2,r3, pc} /* return hex value in R0 */ nxt-firmware-1.29.7/armdebug/Debugger/debug_internals.h000066400000000000000000000341311466344546000231030ustar00rootroot00000000000000/** @file debug_internals.h * @brief Shared C/ASM header file for debugger internal constants * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #ifndef __DEBUG_INTERNALS_H__ #define __DEBUG_INTERNALS_H__ #include "_c_arm_macros.h" /** @addtogroup debugger */ /*@{*/ /* Declarations go here. */ /** @name Debug Message Constants. * * Debug Message Values */ /*@{*/ /* * USB Buffer Sizes: Ctrl Intr Iso Bulk * Full Speed Device 64 64 1023 64 * High Speed Device 64 1024 1024 512 */ #define USB_BUFSIZE 64 /* USB Buffer size for AT91SAM7S */ #define NXT_MSG_TELEGRAMTYPE_OFFSET 0 /* NXT Direct Command/Response Header */ #define NXT_MSG_SEGNUM_OFFSET 1 #define NXT_MSG_TELEGRAMSIZE_OFFSET 2 #define NXT_GDBMSG_TELEGRAMTYPE 0x8d /* GDB debugger specific, no Response required */ #define NXT_GDBMSG_START 3 /* Offset into USB Telegram buffer */ #define MSG_NUMSEGMENTS 3 /* For packet transfers */ #define MSG_SEGMENTSIZE (USB_BUFSIZE - NXT_GDBMSG_START) /* 61 bytes per segment */ #define MSGBUF_SIZE (MSG_SEGMENTSIZE*MSG_NUMSEGMENTS) /* Debug Message Buffer Size, 61 x 3 = 183 chars = ~80 bytes of actual data */ #define MSGBUF_CHKSUMOFFSET 3 /* to be subtracted from message length */ #define MSGBUF_IN_OVERHEADLEN 5 /* For calculating max message data length (exclude ASCIIZ char) */ #define MSGBUF_OUT_OVERHEADLEN 5 /* For calculating max message data length (exclude ASCIIZ char) */ #define MSGBUF_CTRLC 0x03 /* For Out of Band Signaling: not implemented yet */ #define MSGBUF_STARTCHAR '$' #define MSGBUF_ACKCHAR '+' #define MSGBUF_NAKCHAR '-' #define MSGBUF_ERRCHAR 'E' #define MSGBUF_SIGCHAR 'S' #define MSGBUF_SETCHAR '=' #define MSGBUF_CHKSUMCHAR '#' #define MSGBUF_SEPCHAR ',' #define MSGBUF_ARGCHAR ':' #define MSGBUF_MSGERROR -1 /*@}*/ /** @name Debug Command Lookup Constants. * * Debug Command Lookup */ /*@{*/ #define CMDINDEX_OUTOFRANGE -1 /*@}*/ /** @name Debug Register Command Constants. * * Debug Register Command */ /*@{*/ #define CMD_REG_NUMREGS 17 #define CMD_REG_GETONE_MINPARAMLEN 1 #define CMD_REG_GETONE_MAXPARAMLEN 2 #define CMD_REG_GETALL_PARAMLEN 0 #define CMD_REG_REGPARAMLEN 8 /* 32-bit ASCII Hex Value */ #define CMD_REG_SETONE_MINPARAMLEN (2 + CMD_REG_REGPARAMLEN) #define CMD_REG_SETONE_MAXPARAMLEN (3 + CMD_REG_REGPARAMLEN) #define CMD_REG_SETALL_PARAMLEN (CMD_REG_NUMREGS*CMD_REG_REGPARAMLEN) #define CMD_KILL_PARAMLEN 0 #define CMD_DETACH_PARAMLEN 0 /*@}*/ /** @name Debug Memory Command Constants. * * Debug Memory Command * FIXME: These limits are not enforced by the GDB client, it truncates addresses and lengths to remove leading '0's * The PARAMLEN constants would probably be removed */ /*@{*/ #define CMD_NUMITEMS_PARAMLEN 4 /* 16-bit ASCII Hex Value */ #define CMD_MEM_READ_PARAMLEN (CMD_REG_REGPARAMLEN + CMD_NUMITEMS_PARAMLEN + 1) /* Address length is equivalent to reg param len */ #define CMD_MEM_WRITE_MINPARAMLEN (CMD_REG_REGPARAMLEN + CMD_NUMITEMS_PARAMLEN + 2) /* Address length is equivalent to reg param len */ #define CMD_MEM_SEPCHAR_OFFSET CMD_REG_REGPARAMLEN /* Address length is equivalent to reg param len */ #define CMD_MEM_MAXOUTBUFLEN (MSGBUF_SIZE - MSGBUF_OUT_OVERHEADLEN) #define CMD_MEM_MAXREADBYTES (CMD_MEM_MAXOUTBUFLEN/2) #define CMD_MEM_MAXINBUFLEN (MSGBUF_SIZE - MSGBUF_IN_OVERHEADLEN) #define CMD_MEM_MAXWRITEBYTES ((CMD_MEM_MAXINBUFLEN - CMD_MEM_WRITE_MINPARAMLEN)/2) /*@}*/ /** @name Debug Continue and Step Command Constants. * * Debug Continue and Step Command */ /*@{*/ #define CMD_CONTINUE_MINPARAMLEN 0 #define CMD_STEP_MINPARAMLEN 0 /*@}*/ /** @name Debug Query Command Constants. * * Debug Query Command */ /*@{*/ #define CMD_QUERY_MINPARAMLEN 0 #define CMD_QUERY_CURRTID_PARAMLEN 1 #define CMD_QUERY_FTINFO_PARAMLEN 11 #define CMD_QUERY_STINFO_PARAMLEN 11 #define CMD_QUERY_CURRTID_CHAR 'C' #define CMD_QUERY_FTINFO_CHAR 'f' #define CMD_QUERY_STINFO_CHAR 's' /*@}*/ /** @name Debug Breakpoint Command Constants. * * Debug Breakpoint Command */ /*@{*/ #define CMD_BKPT_INSERT_MINPARAMLEN 5 #define CMD_BKPT_REMOVE_MINPARAMLEN 5 #define CMD_BKPT_TYPE_BREAK_MEMORY 0 #define CMD_BKPT_TYPE_BREAK_HARD 1 /* Not supported */ #define CMD_BKPT_TYPE_WATCH_WRITE 2 /* Not supported (yet) */ #define CMD_BKPT_TYPE_WATCH_READ 3 /* Not supported (yet) */ #define CMD_BKPT_TYPE_WATCH_ACCESS 4 /* Not supported (yet) */ #define CMD_BKPT_KIND_THUMB 2 #define CMD_BKPT_KIND_THUMB2 3 /* Not supported */ #define CMD_BKPT_KIND_ARM 4 #define CMD_BKPT_NOTFOUND -1 /*@}*/ /** @name Debug Stack Constants. * * Debug Stack Manipulation Values */ /*@{*/ #define DBGSTACK_NEXTINSTR_INDEX 0 /* Next Instruction Address is at index 0 from bottom of Debug Stack */ #define DBGSTACK_USERCPSR_INDEX 1 /* User CPSR (SPSR_UNDEF) is at index 1 from bottom of Debug Stack */ #define DBGSTACK_USERREG_INDEX 2 /* R0 starts at index 2 from bottom of Debug Stack */ #define DBGSTACK_USERSP_INDEX (DBGSTACK_USERREG_INDEX + REG_SP) /* SP is R13 */ #define DBGSTACK_USERLR_INDEX (DBGSTACK_USERREG_INDEX + REG_LR) /* LR is R14 */ #define DBGSTACK_USERPC_INDEX (DBGSTACK_USERREG_INDEX + REG_PC) /* PC is R15 */ /*@}*/ /** @name Exception Handler Vector Definitions. * * Exception Handler Vectors. */ /*@{*/ #define RESET_VECTOR 0x00000000 #define UNDEF_VECTOR 0x00000004 #define SVC_VECTOR 0x00000008 #define PABRT_VECTOR 0x0000000C #define DABRT_VECTOR 0x00000010 #define RESERVED_VECTOR 0x00000014 #define IRQ_VECTOR 0x00000018 #define FIQ_VECTOR 0x0000001C /*@}*/ /** @name Bitmask Definitions. * * Various Bitmasks used for data manipulation. */ /*@{*/ #define BKPT_STATE_THUMB_FLAG 0x01 /* Flag Thumb Breakpoint */ #define ASCII_LOWER2UPPER_MASK 0x20 /* ASCII Conversion bitmask */ #define NIBBLE0 0x0000000F /* Nibble 0 word(3:0) */ #define NIBBLE1 0x000000F0 /* Nibble 1 word(7:4) */ #define NIBBLE2 0x00000F00 /* Nibble 2 word(11:8) */ #define NIBBLE3 0x0000F000 /* Nibble 3 word(15:12) */ #define NIBBLE4 0x000F0000 /* Nibble 4 word(19:16) */ #define NIBBLE5 0x00F00000 /* Nibble 5 word(23:20) */ #define NIBBLE6 0x0F000000 /* Nibble 6 word(27:24) */ #define NIBBLE7 0xF0000000 /* Nibble 7 word(31:28) */ #define BYTE0 0x000000FF /* Byte 0 word(7:0) */ #define BYTE1 0x0000FF00 /* Byte 1 word(15:8) */ #define BYTE2 0x00FF0000 /* Byte 2 word(23:16) */ #define BYTE3 0xFF000000 /* Byte 3 word(31:24) */ #define HLFWRD0 0x0000FFFF /* Halfword 0 word(15:0) */ #define HLFWRD1 0xFFFF0000 /* Halfword 0 word(31:16) */ /*@}*/ /** @name CPSR Bit Definitions. * * Various Bit definitions for accessing the CPSR register. */ /*@{*/ #define CPSR_THUMB 0x00000020 #define CPSR_FIQ 0x00000040 #define CPSR_IRQ 0x00000080 #define CPSR_MODE 0x0000001F #define CPSR_COND 0xF0000000 /* ARM Exception Modes */ #define MODE_USR 0x10 /* User mode */ #define MODE_FIQ 0x11 /* FIQ mode */ #define MODE_IRQ 0x12 /* IRQ mode */ #define MODE_SVC 0x13 /* Supervisor mode */ #define MODE_ABT 0x17 /* Abort mode */ #define MODE_UND 0x1B /* Undefined mode */ #define MODE_SYS 0x1F /* System mode */ /* Condition Flags * b31 b30 b29 b28 * N Z C V */ #define CPSR_NFLAG 0x80000000 #define CPSR_ZFLAG 0x40000000 #define CPSR_CFLAG 0x20000000 #define CPSR_VFLAG 0x10000000 /* * ARM Opcode Masks (for Parser) */ #define ARM_DATA_INSTR_MASK 0x0FBF0000 #define ARM_DATA_INSTR_MSRMRS 0x010F0000 #define ARM_DATA_INSTR_NORMAL 0x01E00000 #define ARM_DATA_INSTR_IMMREG 0x02000000 #define ARM_LDR_INSTR_REGIMM 0x02000000 #define ARM_LDR_INSTR_PREPOST 0x01000000 #define ARM_LDR_INSTR_UPDOWN 0x00800000 #define ARM_LDM_INSTR_PREPOST 0x01000000 #define ARM_LDM_INSTR_UPDOWN 0x00800000 #define ARM_BLX_INSTR_MASK 0xFE000000 #define ARM_BLX_INSTR_BLX 0xFA000000 #define ARM_BLX_INSTR_HBIT 0x01000000 #define ARM_SWI_INSTR_MASK 0x0F000000 #define ARM_SWI_INSTR_VAL 0x0F000000 /* * Thumb Opcode Masks (for Parser) */ #define THUMB_BLX_INSTR_REG_RNMASK 0x0078 #define THUMB_BCOND_SWI_INSTR_CONDMASK 0x0F00 #define THUMB_BCOND_SWI_COND_UNUSED 0x0E00 #define THUMB_BCOND_SWI_INSTR_SWI 0x0F00 #define THUMB_BLX_INSTR_IMM_HBIT 0x0800 #define THUMB_BLX_INSTR_IMM_MASK 0xF000 #define THUMB_BLX_INSTR_IMM_BL 0xF000 #define THUMB_BLX_INSTR_IMM_BLX 0xE000 /*@}*/ /** Debugger State Enums * * Debugger State. * The enums must be consecutive, starting from 0 */ ENUM_BEGIN ENUM_VALASSIGN(DBG_RESET, 0) /**< Initial State. */ ENUM_VAL(DBG_INIT) /**< Debugger Initialized. */ ENUM_VAL(DBG_CONFIGURED) /**< Debugger has been configured by GDB Server */ ENUM_END(dbg_state_t) /** Breakpoint Type Enums * * Breakpoint Type. * The enums must be consecutive, starting from 0 */ ENUM_BEGIN ENUM_VALASSIGN(DBG_AUTO_BKPT,0) /**< RESERVED: Auto Breakpoint (Instruction resume after breakpoint). */ ENUM_VAL(DBG_MANUAL_BKPT_ARM) /**< Manual ARM Breakpoint. */ ENUM_VAL(DBG_NORMAL_BKPT_ARM) /**< Normal ARM Breakpoint (Single Step, Normal). */ ENUM_VAL(DBG_MANUAL_BKPT_THUMB) /**< Manual Thumb Breakpoint. */ ENUM_VAL(DBG_NORMAL_BKPT_THUMB) /**< Normal Thumb Breakpoint (Single Step, Normal). */ ENUM_VAL(DBG_ABORT_PREFETCH) /**< Prefetch Abort. */ ENUM_VAL(DBG_ABORT_DATA) /**< Data Abort. */ ENUM_END(bkpt_type_t) /** Debugger Message Signal Enums * * Debugger Signal Message Enums. * The enums must be consecutive, starting from 0 */ /* Need to sync with the Signal enums in ecos-common-hal_stub.c */ ENUM_BEGIN ENUM_VALASSIGN(MSG_SIG_DEFAULT, 0) /**< Default Signal Response. */ ENUM_VAL(MSG_SIG_HUP) /**< Hangup Signal Response. */ ENUM_VAL(MSG_SIG_INT) /**< Interrupt Signal Response. */ ENUM_VAL(MSG_SIG_QUIT) /**< Quit Signal Response. */ ENUM_VAL(MSG_SIG_ILL) /**< Illegal Instruction Signal Response (not reset when caught). */ ENUM_VAL(MSG_SIG_TRAP) /**< Trace Trap Signal Response (not reset when caught). */ ENUM_VAL(MSG_SIG_ABRT) /**< Abort Signal Response (replace SIGIOT). */ ENUM_VAL(MSG_SIG_EMT) /**< EMT Instruciton Signal Response. */ ENUM_VAL(MSG_SIG_FPE) /**< Floating Point Exception Signal Response. */ ENUM_VAL(MSG_SIG_KILL) /**< Kill Signal Response (cannot be caught or ignored). */ ENUM_VAL(MSG_SIG_BUS) /**< Bus Error Signal Response. */ ENUM_VAL(MSG_SIG_SEGV) /**< Segmentation Violation Signal Response. */ ENUM_VAL(MSG_SIG_SYS) /**< Bad Argument to System Call Signal Response. */ ENUM_VAL(MSG_SIG_PIPE) /**< Write on a Pipe with No Reader Signal Response. */ ENUM_VAL(MSG_SIG_ALRM) /**< Alarm Clock Signal Response. */ ENUM_VAL(MSG_SIG_TERM) /**< Software Termination Signal from Kill Signal Response. */ ENUM_END(dbg_msg_signo) /** Debugger Message Error Enums * * Debugger Error Message Enums. * The enums must be consecutive, starting from 1 */ /* FIXME: Need to validate against the ecos-generic-stub.c Error enums */ ENUM_BEGIN ENUM_VALASSIGN(MSG_ERRIMPL, 0) /**< Stub (not implemented) Error. */ ENUM_VAL(MSG_ERRINLENGTH) /**< Message Write Length Error. */ ENUM_VAL(MSG_ERROUTLENGTH) /**< Message Read Length Error. */ ENUM_VAL(MSG_ERRFORMAT) /**< Message Format Error. */ ENUM_VAL(MSG_UNKNOWNCMD) /**< Unrecognized Command Error. */ ENUM_VAL(MSG_UNKNOWNPARAM) /**< Unrecognized Parameter Error. */ ENUM_VAL(MSG_UNKNOWNBRKPT) /**< Unrecognized Breakpoint Error. */ ENUM_END(dbg_msg_errno) /** Register Enums * * Register Enums. * Refer to eCOS's arm_stub.h for enum values */ ENUM_BEGIN ENUM_VALASSIGN(REG_R0, 0) /**< User Reg R0 */ ENUM_VAL(REG_R1) /**< User Reg R1 */ ENUM_VAL(REG_R2) /**< User Reg R2 */ ENUM_VAL(REG_R3) /**< User Reg R3 */ ENUM_VAL(REG_R4) /**< User Reg R4 */ ENUM_VAL(REG_R5) /**< User Reg R5 */ ENUM_VAL(REG_R6) /**< User Reg R6 */ ENUM_VAL(REG_R7) /**< User Reg R7 */ ENUM_VAL(REG_R8) /**< User Reg R8 */ ENUM_VAL(REG_R9) /**< User Reg R9 */ ENUM_VAL(REG_R10) /**< User Reg R10 */ ENUM_VAL(REG_R11) /**< User Reg R11 */ ENUM_VAL(REG_R12) /**< User Reg R12 */ ENUM_VAL(REG_SP) /**< Previous Mode SP (R13) */ ENUM_VAL(REG_LR) /**< Previous Mode LR (R14) */ ENUM_VAL(REG_PC) /**< Program Counter (R15) */ ENUM_VALASSIGN(REG_FPSCR, 24) /**< Previous Mode FPSCR (dummy) */ ENUM_VAL(REG_CPSR) /**< Previous Mode CPSR */ ENUM_END(register_enum_t) /** Abort Type Enums * * Abort Type used for interfacing with LCD Display routine. * The enums must be consecutive, starting from 0 * Note: The values must align with those defined in NxOS's _abort.h */ ENUM_BEGIN ENUM_VALASSIGN(DISP_ABORT_PREFETCH,0) /**< Prefetch Abort. */ ENUM_VAL(DISP_ABORT_DATA) /**< Data Abort. */ ENUM_VAL(DISP_ABORT_SPURIOUS) /**< Spurious IRQ. */ ENUM_VAL(DISP_ABORT_ILLEGAL) /**< Illegal Instruction. */ ENUM_END(abort_type_t) /*@}*/ #endif /* __DEBUG_INTERNALS_H__ */ nxt-firmware-1.29.7/armdebug/Debugger/debug_macros.h000066400000000000000000000336111466344546000223720ustar00rootroot00000000000000/** @file debug_macros.h * @brief internal macros used by debug_stub routines * */ /* Copyright (C) 2007-2010 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #ifndef __DEBUG_MACROS_H__ #define __DEBUG_MACROS_H__ /*@{*/ /** _dbg_jumpTableHandler * Call Jump Table Routine based on Index * On entry: * @param jumptableaddr is the address (constant) of the Jump Table * @param jumpreg is the register used to perform the indirect jump * @param indexreg contains jump table index value */ .macro _dbg_jumpTableHandler jumptableaddr, jumpreg, indexreg ldr \jumpreg, =\jumptableaddr ldr \jumpreg, [\jumpreg, \indexreg, lsl #2] mov lr, pc bx \jumpreg /* Call Command Handler Routine */ .endm /** _dbg_thumbDecodeEntry * Load Thumb Instruction Decoder Entry * On entry: * @param instrreg is the register to load the instruction into * @param instrmask is the register to load the instruction mask into * @param codehandler is the register to load the code handling routine into * @param indexreg contains decode table index value * NOTE: instrreg, instrmask, codehandler must be in increasing register number order */ .macro _dbg_thumbDecodeEntry instrreg, instrmask, codehandler, indexreg ldr \instrmask, =debug_thumbDecodeTable /* Temporary register */ add \instrmask, \instrmask, \indexreg, lsl #3 ldm \instrmask, {\instrreg, \codehandler} /* LSHW: IID, MSHW: IBM */ mov \instrmask, \instrreg, lsr #16 mov \instrreg, \instrreg, lsl #16 mov \instrreg, \instrreg, lsr #16 /* Keep HLFWORD0 containing instruction */ .endm /** _dbg_armDecodeEntry * Load ARM Instruction Decoder Entry * On entry: * @param instrreg is the register to load the instruction into * @param instrmask is the register to load the instruction mask into * @param codehandler is the register to load the code handling routine into * @param indexreg contains decode table index value * NOTE: instrreg, instrmask, codehandler must be in increasing register number order */ .macro _dbg_armDecodeEntry instrreg, instrmask, codehandler, indexreg ldr \instrmask, =debug_armDecodeTable /* Temporary register */ add \instrmask, \instrmask, \indexreg, lsl #3 add \instrmask, \instrmask, \indexreg, lsl #2 /* 12 byte entries */ ldm \instrmask, {\instrreg, \instrmask, \codehandler} .endm /** _asciiz * Terminate string given string buffer pointer in \strptr * scratchreg is used as a scratch register (destroyed) * */ .macro _asciiz strptr, scratchreg mov \scratchreg, #0 /* ASCIIZ character */ strb \scratchreg, [\strptr] /* Terminate ASCII string */ .endm /** _dbg_stpcpy * _dbg_stpcpy macro * On entry: * deststrptr: Destination string * sourcestrptr: Source string * scratchreg: scratch register for copying * On exit: * deststrptr: Pointer to ASCIIZ character in destination string * sourcestrptr: Pointer to next character slot in source string (after ASCIIZ) * scratchreg: destroyed */ .macro _dbg_stpcpy deststrptr, sourcestrptr, scratchreg 1: ldrb \scratchreg, [\sourcestrptr], #1 strb \scratchreg, [\deststrptr], #1 teq \scratchreg, #0 bne 1b sub \deststrptr, \deststrptr, #1 /* Adjust Destination string pointer to point at ASCIIZ character */ .endm /** _dbg_memcpy * _dbg_stpcpy macro * On entry: * deststrptr: Destination string * sourcestrptr: Source string * sizereg: Number of bytes to copy * scratchreg: scratch register for copying * On exit: * deststrptr: Pointer to next character slot in destination string * sourcestrptr: Pointer to next character slot in source string * sizereg, scratchreg: destroyed */ .macro _dbg_memcpy deststrptr, sourcestrptr, sizereg, scratchreg 1: ldrb \scratchreg, [\sourcestrptr], #1 strb \scratchreg, [\deststrptr], #1 subs \sizereg, \sizereg, #1 bne 1b .endm /** _dbg_CopyMsg2OutputBuf * Copies source message to output buffer * On entry: * R2: source message buffer (ASCIIZ terminated) * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R2-R3: destroyed */ .macro _dbg_CopyMsg2OutputBuf ldr r0, =debug_OutMsgBuf _dbg_stpcpy r0, r2, r3 .endm /** _dbg_CopyMsg2OutputBuf_withParam * Internal Routine called to output message with parameters * Return Message with byte-sized parameter * On entry: * R1: byte-sized param * R2: source message buffer (ASCIIZ terminated) * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R1-R3: destroyed */ .macro _dbg_CopyMsg2OutputBuf_withParam _dbg_CopyMsg2OutputBuf /* R1 unchanged */ bl byte2ascii /* R0 points to buffer position after byte value */ _asciiz r0, r1 .endm /** _dbg_outputAckOnlyFlag * Return Flag ('+') for Continue or Step * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R2-R3: destroyed */ .macro _dbg_outputAckOnlyFlag ldr r2, =debug_AckOnlyFlag /* ASCIIZ terminated */ _dbg_CopyMsg2OutputBuf .endm /** _dbg_outputRetransmitFlag * Return Flag ('-') for Checksum Error (retransmission needed) * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R2-R3: destroyed */ .macro _dbg_outputRetransmitFlag ldr r2, =debug_RetransmitFlag /* ASCIIZ terminated */ _dbg_CopyMsg2OutputBuf .endm /** _dbg_outputMsgValidResponse * Return Message with valid response ('+$') * On exit: * R0: Pointer to Output Buffer next character slot location * R2-R3: destroyed */ .macro _dbg_outputMsgValidResponse ldr r2, =debug_ValidResponsePrefix _dbg_CopyMsg2OutputBuf .endm /** _dbg_outputMsgStatusOk * Return Message with Ok ('+$OK') status * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R2-R3: destroyed */ .macro _dbg_outputMsgStatusOk ldr r2, =debug_OkResponse /* ASCIIZ terminated */ _dbg_CopyMsg2OutputBuf .endm /** _dbg_outputMsgCurrTID * Return Message with Default Thread ID ('+$QC0') * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R2-R3: destroyed */ .macro _dbg_outputMsgCurrTID ldr r2, =debug_ThreadIDResponse /* ASCIIZ terminated */ _dbg_CopyMsg2OutputBuf .endm /** _dbg_outputMsgFirstThreadInfo * Return Message with Default Current Thread ID ('+$m0') * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R2-R3: destroyed */ .macro _dbg_outputMsgFirstThreadInfo ldr r2, =debug_FirstThreadInfoResponse /* ASCIIZ terminated */ _dbg_CopyMsg2OutputBuf .endm /** _dbg_outputMsgSubsequentThreadInfo * Return Message with Default Current Thread ID ('+$m0') * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R2-R3: destroyed */ .macro _dbg_outputMsgSubsequentThreadInfo ldr r2, =debug_SubsequentThreadInfoResponse /* ASCIIZ terminated */ _dbg_CopyMsg2OutputBuf .endm /** _dbg_outputMsgStatusErr * Return Message with Error ('+$ENN') status * On entry: * R1: error code * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R1-R3: destroyed */ .macro _dbg_outputMsgStatusErr ldr r2, =debug_ErrorResponsePrefix _dbg_CopyMsg2OutputBuf_withParam .endm /** _dbg_outputMsgStatusErrCode * Return Message with Error ('+$ENN') status * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R1-R3: destroyed */ .macro _dbg_outputMsgStatusErrCode errcode mov r1, #\errcode _dbg_outputMsgStatusErr .endm /** _dbg_outputMsgStatusSig * Return Message with Signal ('+$SNN') status * On entry: * R1: signal code * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R1-R3: destroyed */ .macro _dbg_outputMsgStatusSig ldr r2, =debug_SignalResponsePrefix _dbg_CopyMsg2OutputBuf_withParam .endm /** _dbg_outputMsgStatusSigCode * Return Message with Signal ('+$SNN') status * On exit: * R0: Pointer to Output Buffer ASCIIZ location * R1-R3: destroyed */ .macro _dbg_outputMsgStatusSigCode statuscode mov r1, #\statuscode _dbg_outputMsgStatusSig .endm /** _regenum2index * Convert register enum to debugger stack index * * On entry: * @param indexenum: enum representing Register to access * @param indexreg: register to be used to store the debugger stack index value (0-max index) * On exit: * @param indexreg contains debugger stack index value (0-max index) */ .macro _regenum2index indexenum, indexreg add \indexreg, \indexenum, #DBGSTACK_USERREG_INDEX /* Convert register index to Debug Stack index */ .endm /** _getdbgregisterfromindex * Retrieve register contents from debugger stack given index * * On entry: * @param indexreg contains debugger stack index value (0-max index) * On exit: * @param indexreg: Breakpoint index (preserved) * @param contentsreg: Register Contents for given index */ .macro _getdbgregisterfromindex indexreg, contentsreg ldr \contentsreg, =__debugger_stack_bottom__ ldr \contentsreg, [\contentsreg, \indexreg, lsl #2] .endm /** _setdbgregisterfromindex * Store register contents to debugger stack given index * * On entry: * @param indexreg contains debugger stack index value (0-max index) * @param contentsreg: Register Contents for given index * @param addressreg: Scratch register for address pointer * On exit: * @param indexreg: Breakpoint index (preserved) * @param contentsreg: Register Contents for given index */ .macro _setdbgregisterfromindex indexreg, contentsreg, addressreg ldr \addressreg, =__debugger_stack_bottom__ str \contentsreg, [\addressreg, \indexreg, lsl #2] .endm /** _getdbgregister * Retrieve register contents from debugger stack given immediate index value * * On entry: * @param indexval contains debugger stack index value (0-max index) * On exit: * @param contentsreg: Register Contents for given index */ .macro _getdbgregister indexval, contentsreg ldr \contentsreg, =__debugger_stack_bottom__ ldr \contentsreg, [\contentsreg, #(\indexval << 2)] .endm /** _setdbgregister * Store register contents to debugger stack given immediate index value * * On entry: * @param indexval contains debugger stack index value (0-max index) * @param contentsreg: Register Contents for given index * @param addressreg: Scratch register for address pointer * On exit: * @param contentsreg: Register Contents for given index * @param addressreg: Destroyed */ .macro _setdbgregister indexval, contentsreg, addressreg ldr \addressreg, =__debugger_stack_bottom__ str \contentsreg, [\addressreg, #(\indexval << 2)] .endm /** _index2bkptindex_addr * Convert Breakpoint index to breakpoing entry address * * On entry: * @param indexreg contains breakpoint index value * On exit: * @param indexreg: Breakpoint index (preserved) * @param addrreg: Breakpoint Entry Address */ .macro _index2bkptindex_addr indexreg, addrreg ldr \addrreg, =__breakpoints_start__ add \addrreg, \addrreg, \indexreg, lsl #3 /* Calculate Breakpoint Entry Address */ .endm /** _dbg_getstate * Get Debugger State * On exit: * @param reg: Debugger State enum */ .macro _dbg_getstate reg ldr \reg, =debug_state ldrb \reg, [\reg] .endm /** _dbg_setstate * Set Debugger State to given value * On exit: * r0, r1: destroyed */ .macro _dbg_setstate state mov r0, #\state ldr r1, =debug_state strb r0, [r1] .endm /** _dbg_getmode * Get Debugger Mode * On exit: * @param reg: Debugger Mode (Boolean) */ .macro _dbg_getmode reg ldr \reg, =debug_mode ldrb \reg, [\reg] .endm /** _dbg_setmode * Set Debugger Mode to given value * On exit: * r0, r1: destroyed */ .macro _dbg_setmode mode mov r0, #\mode ldr r1, =debug_mode strb r0, [r1] .endm /** _dbg_get_bkpt_type * Get Breakpoint Type * On exit: * @param reg: Breakpoint Type */ .macro _dbg_get_bkpt_type reg ldr \reg, =debug_bkpt_type ldrb \reg, [\reg] .endm /** _dbg_set_bkpt_type * Set Breakpoint Type using value in reg * On exit: * @param reg: destroyed * r1: destroyed */ .macro _dbg_set_bkpt_type reg ldr r1, =debug_bkpt_type strb \reg, [r1] .endm /** _dbg_set_bkpt_type_val * Set Breakpoint Type to given value * On exit: * r0, r1: destroyed */ .macro _dbg_set_bkpt_type_val bkpt_type mov r0, #\bkpt_type ldr r1, =debug_bkpt_type strb r0, [r1] .endm /** _dbg_getcurrbkpt_index * Get current breakpoint index * On exit: * @param reg: Breakpoint index */ .macro _dbg_getcurrbkpt_index reg ldr \reg, =debug_curr_breakpoint ldrb \reg, [\reg] .endm /** _dbg_setcurrbkpt_index * Set current breakpoint index * On exit: * r1: destroyed */ .macro _dbg_setcurrbkpt_index reg ldr r1, =debug_curr_breakpoint strb \reg, [r1] .endm /*@}*/ #endif /* __DEBUG_MACROS_H__ */ nxt-firmware-1.29.7/armdebug/Debugger/debug_opcodes.S000066400000000000000000001220701466344546000225130ustar00rootroot00000000000000/** @file debug_opcodes.S * @brief ARM Debugger Opcode Parsing Routines * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #define __ASSEMBLY__ #include "debug_stub.h" #include "debug_internals.h" #include "debug_macros.h" .data .align 4 /* Rm Shifted Shift Type Jump Table * On entry: * R0: Register Rm * R1: Shift/Rotate Amount * On exit: * R0: RmShifted result * */ debug_regShiftJumpTable: .word _reg_lsl /* 00 */ .word _reg_lsr /* 01 */ .word _reg_asr /* 02 */ .word _reg_ror /* 03 */ .word _reg_rrx /* 04 */ /* Data Processing Instruction Jump Table * On entry: * R0: Register Rn (Operand 1) value * R1: Operand 2 value * R2: Default Next Instruction Address * R5[3:0]: CPSR condition codes * On exit: * R0: Calculated result * R1, R2, R3: Destroyed * */ debug_dataInstrJumpTable: .word _opcode_and /* 00 */ .word _opcode_eor /* 01 */ .word _opcode_sub /* 02 */ .word _opcode_rsb /* 03 */ .word _opcode_add /* 04 */ .word _opcode_adc /* 05 */ .word _opcode_sbc /* 06 */ .word _opcode_rsc /* 07 */ .word _opcode_tst /* 08 */ .word _opcode_teq /* 09 */ .word _opcode_cmp /* 0A */ .word _opcode_cmn /* 0B */ .word _opcode_orr /* 0C */ .word _opcode_mov /* 0D */ .word _opcode_bic /* 0E */ .word _opcode_mvn /* 0F */ /* * To determine the next instruction to execute, we need to check current (breakpointed) instruction * and determine whether it will be executed or not. This necessitates a mini instruction decoder * that can check the type of instruction, as well as if it'll affect the PC. * The instruction decoder used here is table based. Each entry in the table consists of: * Instruction Identifier (IID), Instruction Bitmask (IBM), Instruction Handler Address (IHA) * Null entries are placed at the end of the table. * * This allows for a flexible approach to handling instructions that we're interested in, at the expense * of memory usage. * * For ARM, the IID & IBM are both 4 bytes, whereas the Thumb IID & IBM are 2 bytes. * The IHA is always 4 bytes. */ /* ARM Instruction Decode Table * .word IID, IBM, IHA (12 bytes) */ /* WARNING: The sequence of matching instructions is important! * Always check from more specific to more general IBMs * for instructions sharing common opcode prefix bits. */ debug_armDecodeTable: .word 0x012fff10, 0x0ffffff0, _arm_bx_blx_handler /* [Prefix:00] BX or BLX. Note v4t does not have BLX instr */ .word 0x0000f000, 0x0c00f000, _arm_data_instr_handler /* [Prefix:00] Data Processing instr with Rd = R15 */ /* .word 0x06000010, 0x0e000010, _arm_undef_handler */ /* [Prefix:01] Undefined instr: shouldn't occur, as it would've been trapped already. See _dbg_following_instruction_addr */ .word 0x0410f000, 0x0410f000, _arm_ldr_pc_handler /* [Prefix:01] LDR with Rd = PC */ .word 0x08108000, 0x0e108000, _arm_ldm_pc_handler /* [Prefix:10] LDM {pc} */ .word 0x0a000000, 0x0e000000, _arm_b_bl_blx_handler /* [Prefix:10] B, BL or BLX. Note v4t does not have BLX instr */ .word 0x0c000000, 0x0c000000, _arm_coproc_swi_handler /* [Prefix:11] Coprocessor instr or SWI */ .word 0x0,0x0,0x0 /* Null Entry */ /* Thumb Instruction Decode Table * .hword IID, IBM * .word IHA (8 bytes) */ /* WARNING: The sequence of matching instructions is important! * Always check from more specific to more general IBMs * for instructions sharing common opcode prefix bits. */ debug_thumbDecodeTable: .hword 0x4700, 0xff07 .word _thumb_bx_blx_handler /* [Prefix:01] BX or BLX. Note: Link (L:b7) is not checked in the mask */ .hword 0xbd00, 0xff00 .word _thumb_poppc_handler /* [Prefix:10] PUSH/POP, specifically POP {Rlist,PC} */ .hword 0xd000, 0xf000 .word _thumb_bcond_swi_handler /* [Prefix:11] B or SWI */ .hword 0xe000, 0xf800 .word _thumb_b_handler /* [Prefix:11] B */ .hword 0xf000, 0xf000 .word _thumb_long_bl_blx_handler /* [Prefix:11] Long BL or BLX (4 bytes) Note: b11 (H) indicates 1st or 2nd instr */ .hword 0x0,0x0 .word 0x0 /* Null Entry */ /* ARM Condition Code Mapping Table * Converts Instruction encoding to SPSR Flags. * b31 b30 b29 b28 * N Z C V * Indexed according to Instruction Encoding order (pg 30, Table 6, ATMEL ARM7TDMI Data Sheet) * Condition Code stored in MSN(set), LSN(clr) order * Note1: 0x00 = AL. NV is deprecated, treat as AL * Note2: 0xFF indicates that the condition checks needs to be handled separately (complex checks) * * EQ: Z set * NE: Z clr * HS/CS: C set * LO/CC: C clr * MI: N set * PL: N clr * VS: V set * VC: V clr */ debug_armCondCodeTable: /* EQ, NE, HS/CS, LO/CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV */ .byte 0x40, 0x04, 0x20, 0x02, 0x80, 0x08, 0x10, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 /* ARM Complex Condition Code Mapping Table * Converts Instruction encoding to SPSR Flags. * b31 b30 b29 b28 * N Z C V * Indexed according to Instruction Encoding order (pg 30, Table 6, ATMEL ARM7TDMI Data Sheet) * for HI, LS, GE, LT, GT and LE instructions only * Condition Code stored in the following order: * b7 b6 b5 b4 b3 b2 b1 b0 * AND CHKZ CHKC CHKNV - Z set C set N==V (bit set = 1) * OR - - - - Z clr C clr N!=V (bit clr = 0) * * HI: C set AND Z clr * LS: C clr OR Z set * GE: N == V * LT: N != V * GT: Z clr AND (N == V) * LE: Z set OR (N != V) */ #define COMPLEX_CONDCODE_START 0x08 #define COMPLEX_CONDCODE_NEQV_MASK 0x01 #define COMPLEX_CONDCODE_CSET_MASK 0x02 #define COMPLEX_CONDCODE_ZSET_MASK 0x04 #define COMPLEX_CONDCODE_CHKNV_MASK 0x10 #define COMPLEX_CONDCODE_CHKC_MASK 0x20 #define COMPLEX_CONDCODE_CHKZ_MASK 0x40 #define COMPLEX_CONDCODE_ANDOR_MASK 0x80 #define COMPLEX_CONDCODE_NFLAG 0x08 #define COMPLEX_CONDCODE_ZFLAG 0x04 #define COMPLEX_CONDCODE_CFLAG 0x02 #define COMPLEX_CONDCODE_VFLAG 0x01 debug_armComplexCCTable: /* HI, LS, GE, LT, GT, LE */ .byte 0xE2, 0x64, 0x11, 0x10, 0xD1, 0x54 .code 32 .text .align 4 /* dbg_following_instruction_addr * Determine the address of the following instruction to execute. * On entry: * R0: Address of the instruction to be (re)executed * On exit: * R0: Destroyed * R1: Following Instruction Address (31 bits, b0 = THUMB flag) * R2-R7: Destroyed * * Here we make use of the Debugger Stack which contains the address of the aborted instruction that will be reexecuted * when we resume the program. * * If it is a Manual Breakpoint inserted into the code, then we will need to update the aborted instruction * address to skip the current aborted instruction and resume execution at the next instruction address, * and the next instruction address to be returned to the calling routine is the following instruction * address instead. * * We need to check the aborted instruction type, to see if it is a branch instruction, before we can determine * the next instruction address (for inserting a Breakpoint). */ .global dbg_following_instruction_addr dbg_following_instruction_addr: stmfd sp!, {lr} /* We assume that any BKPT instructions in the code will be Manual Breakpoints, * i.e., the Debugger does not leave stray Single Step / Auto / Normal breakpoints in memory */ mov r6, r0 /* Keep instruction address in R6 */ _getdbgregister DBGSTACK_USERCPSR_INDEX, r1 /* Retrieve User CPSR into R1 */ and r0, r1, #CPSR_THUMB /* store Thumb Mode status in R0 */ mov r5, r1, lsr #28 /* store CPSR condition flags in R5[3:0] */ _dbg_get_aborted_instr: 1: teq r0, #0 /* Check if it is ARM or Thumb instruction */ ldrneh r4, [r6] /* Load Thumb instruction opcode using Addr in R6 into R4 */ ldrne r2, =(BKPT16_INSTR | BKPT16_MANUAL_BKPT) /* check for Thumb Manual Breakpoint Instruction */ ldreq r4, [r6] /* Load ARM instruction opcode using Addr in R6 into R4 */ ldreq r2, =(BKPT32_INSTR | BKPT32_MANUAL_BKPT) /* check for ARM Manual Breakpoint Instruction */ teq r4, r2 /* Is instruction opcode (R4) == Manual Breakpoint opcode (R2)? */ bne 2f /* Not Manual breakpoint */ teq r0, #0 /* Check if it is ARM or Thumb Manual Breakpoint */ addne r6, r6, #2 /* Is Manual Breakpoint, Skip to next Thumb instruction */ addeq r6, r6, #4 /* Is Manual Breakpoint, Skip to next ARM instruction */ b 1b /* To protect against a sequence of Manual Breakpoint Instructions */ /* Here, R4 contains the instruction opcode which will be (re)executed when program resumes. * We need to dissect it to see if it is a branch instruction. * For ARM instructions, we also need to evaluate the current (breakpointed) instruction to see if it'll execute. * If not, then the following instruction is at the address following the address of the opcode in R4 (Default Following Instruction Address in R6). */ 2: teq r0, #0 /* Check if current instruction is ARM or Thumb instruction */ beq _following_instr_addr_for_arm _following_instr_addr_for_thumb: add r6, r6, #2 /* Store default following Thumb instruction address to R6 */ #if 0 /* Flag Thumb instruction only within the instruction handler */ orr r6, r6, #BKPT_STATE_THUMB_FLAG /* Set b0 to indicate Thumb instruction */ #endif /* R4: Candidate Instruction Opcode * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+2) */ bl _eval_thumb_instruction /* following address is either ARM or Thumb */ /* We must set this the Thumb bit only within the instruction handler since BX would switch modes */ b _exit_dbg_following_instruction_addr _following_instr_addr_for_arm: add r6, r6, #4 /* Store default following ARM instruction address to R6 */ /* R4: Candidate Instruction Opcode * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) */ bl _eval_arm_instruction _exit_dbg_following_instruction_addr: mov r1, r0 /* Return Actual Following Instruction Address in R1 (B0 set to indicate Thumb mode) */ ldmfd sp!, {pc} /* _eval_arm_instruction * Evaluate ARM instruction to determine following instruction address * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1-R7: destroyed */ _eval_arm_instruction: stmfd sp!, {lr} bl _dbg_check_arm_condcode /* Returns R0: will_execute (boolean) */ teq r0, #FALSE moveq r0, r6 /* If False (don't execute), so use Default Following Instruction Address */ beq _exit_eval_arm_instruction /* and Return to caller */ _will_execute_arm_instr: mov r0, #0 /* initialize ARM Decode Entry Table index register */ 1: _dbg_armDecodeEntry r1, r2, r3, r0 /* instrreg (R1), instrmask (R2), codehandler (R3), indexreg (R0) */ teq r1, #0 /* Check for Null Entry (End of Table marker) */ moveq r0, r6 /* End of Table, no match found, so use Default Following Instruction Address */ beq _exit_eval_arm_instruction and r7, r4, r2 /* Use R7 to check masked instruction opcode (from R4) to see if it matches template (in R1) */ teq r7, r1 addne r0, r0, #1 /* No match, so keep looking */ bne 1b _call_arm_code_handler: mov lr, pc bx r3 /* Call Code Handler with R4: Instruction Opcode, R5[3:0]: CPSR, R6: Default Following Instruction Address */ _exit_eval_arm_instruction: /* Returned Following Address Instruction in R0 (B0 set to indicate Thumb mode) */ ldmfd sp!, {pc} /* _eval_thumb_instruction * Evaluate Thumb instruction to determine following instruction address * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+2) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1-R7: destroyed */ _eval_thumb_instruction: stmfd sp!, {lr} /* Only B instructions are conditionally executed, deal with it in that Code Handler */ mov r0, #0 /* initialize Thumb Decode Entry Table index register */ 1: _dbg_thumbDecodeEntry r1, r2, r3, r0 /* instrreg (R1), instrmask (R2), codehandler (R3), indexreg (R0) */ teq r1, #0 /* Check for Null Entry (End of Table marker) */ moveq r0, r6 /* End of Table, no match found, so use Default Following Instruction Address */ orreq r0, r0, #BKPT_STATE_THUMB_FLAG /* Set R0[0] to flag Thumb mode */ beq _exit_eval_thumb_instruction and r7, r4, r2 /* Use R5 to check masked instruction opcode (from R4) to see if it matches template (in R1) */ teq r7, r1 addne r0, r0, #1 /* No match, so keep looking */ bne 1b _call_thumb_code_handler: mov lr, pc bx r3 /* Call Code Handler with R4: Instruction Opcode, R5[3:0]: CPSR, R6: Default Following Instruction Address */ _exit_eval_thumb_instruction: /* Returned Following Address Instruction in R0 */ ldmfd sp!, {pc} /**************************************************************************** * * Instruction Decode Routines * ****************************************************************************/ /* _dbg_check_arm_condcode * Check ARM conditional execution code * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * On exit: * R0: will_execute (boolean) * R1-R3: Destroyed */ _dbg_check_arm_condcode: mov r0, #TRUE /* Default will_execute value */ mov r3, r4, lsr #28 /* convert opcode's condition code to index (0-F) */ ldr r2, =debug_armCondCodeTable ldrb r1, [r2, r3] /* Get condition code mask */ /* * The following check is unnecessary as it is covered by the _dbg_cond_simple_checks checking algorithm teq r1, #0 beq _dbg_check_arm_condcode_exit */ teq r1, #0xFF bne _dbg_cond_simple_checks /* * Complex Checks: * We assume that CHKNV and CHKC are mutually exclusive. * In addition, it is possible for CHKNV, CHKC and CHKZ to * be cleared, in which case it'll return True (default) * * * will_execute = TRUE [default condition] * If (CHKNV) set * // Only N/V, and Z flags are involved * NEQV_Flag = (N == V) * will_execute = (NEQV_Flag == NEQV_Mask) * * If (CHKC) set * // Only C and Z flags are involved * will_execute = (C_Flag == CSet_Mask) * * If (CHKZ) set * z_match = (Z_Flag == ZSet_Mask) * If (AND bit set) * will_execute = will_execute && z_match * else * will_execute = will_execute || z_match * */ _dbg_cond_complex_checks: sub r3, r3, #COMPLEX_CONDCODE_START /* Convert complex condition code in R3 to new index (0-3) */ ldr r2, =debug_armComplexCCTable ldrb r1, [r2, r3] /* Get complex condition code bitmap in R1 */ _cond_check_nv: tst r1, #COMPLEX_CONDCODE_CHKNV_MASK beq _cond_check_c /* CHECKNV not set, so skip */ ands r2, r5, #(COMPLEX_CONDCODE_NFLAG | COMPLEX_CONDCODE_VFLAG) /* Is (N == V == 0)? */ teqne r2, #(COMPLEX_CONDCODE_NFLAG | COMPLEX_CONDCODE_VFLAG) /* No, Is (N == V == 1)? */ moveq r2, #COMPLEX_CONDCODE_NEQV_MASK /* EQ: Either (N == V == 0) or (N == V == 1), set R2: COMPLEX_CONDCODE_NEQV_MASK */ movne r2, #0 /* NE: N != V, clear R2 */ and r3, r1, #COMPLEX_CONDCODE_NEQV_MASK /* R3: Extract NEQV Mask Value */ teq r2, r3 /* Does N/V Condition match NEQV Mask value? */ movne r0, #FALSE /* No, so will_execute = FALSE (for now) */ b _cond_check_z #if 0 bne _cond_nnev /* No, so (N != V) */ /* EQ: Either (N == V == 0) or (N == V == 1) */ _cond_neqv: tst r1, #COMPLEX_CONDCODE_NEQV_MASK /* Is (N == V) mask set? */ moveq r0, #FALSE /* No, so will_execute = FALSE (for now) */ b _cond_check_z /* Else, N != V */ _cond_nnev: tst r1, #COMPLEX_CONDCODE_NEQV_MASK /* Is (N == V) mask set? */ movne r0, #FALSE /* Yes, so will_execute = FALSE (for now) */ b _cond_check_z #endif _cond_check_c: tst r1, #COMPLEX_CONDCODE_CHKC_MASK beq _cond_check_z /* CHECKC not set, so skip */ /* Use R2 to store C Flag, R3 to store CSet Mask */ and r2, r5, #COMPLEX_CONDCODE_CFLAG /* r2 = C flag */ and r3, r1, #COMPLEX_CONDCODE_CSET_MASK /* r3 = CSet mask */ teq r2, r3 /* Does C flag == CSet mask */ movne r0, #FALSE /* No, so C flag failed match */ _cond_check_z: tst r1, #COMPLEX_CONDCODE_CHKZ_MASK beq _dbg_check_arm_condcode_exit /* No additional checks needed, exit */ /* Use R2 to store Z Flag, R3 to store ZSet Mask */ and r2, r5, #COMPLEX_CONDCODE_ZFLAG /* r2 = Z flag */ and r3, r1, #COMPLEX_CONDCODE_ZSET_MASK /* r3 = ZSet mask */ teq r2, r3 /* Does Z flag == ZSet mask */ moveq r3, #TRUE /* Zero, so z flag matched */ movne r3, #FALSE /* Non-zero, so z flag failed match */ _cond_andor: tst r1, #COMPLEX_CONDCODE_ANDOR_MASK /* Is ANDOR mask set? */ andne r0, r0, r3 /* Yes, so AND with will_execute */ orreq r0, r0, r3 /* No, so OR with will_execute */ b _dbg_check_arm_condcode_exit /* Return will_execute (R0) */ /* * Simple Checks: * We take advantage of the fact that only 1 bit would be set * in the bitmask, by generating the corresponding actual * CondSet[7:4], CondClr[3:0] value for comparison. * * will_execute = TRUE [default condition, equivalent to 0x00 (AL) ] * Generate CondSetClr[7:0] from CPSR[3:0] * will_execute = ((CondSetClr & BitMask) == BitMask) * */ _dbg_cond_simple_checks: eor r2, r5, #NIBBLE0 /* R2: CondClr[3:0] = Invert CPSR[3:0] */ orr r2, r2, r5, lsl #4 /* R2: CondSet[7:4] | CondClr[3:0] */ and r2, r2, r1 /* R2: CondSetClr[7:0] & Bitmask */ teq r2, r1 /* ((cond_code & SetBitMask) == SetBitMask)? */ movne r0, #FALSE /* Not equal, check failed */ _dbg_check_arm_condcode_exit: bx lr /* Return to caller */ /* _arm_rmshifted_val * Calculate value of Shifted Rm (operand) * On entry: * R0[11:0]: Shifted Rm operand * On exit: * R0: value of Shifted Rm * R1, R2, R3: destroyed */ _arm_rmshifted_val: stmfd sp!, {lr} ldr r3, =(NIBBLE2|BYTE0) and r3, r0, r3 /* 12 bit Shifted Register operand, copied to R3 */ and r2, r3, #NIBBLE0 /* Register Rn Enum in R2 */ _regenum2index r2, r2 /* Convert Enum into Index in R2 */ _getdbgregisterfromindex r2, r0 /* Retrieve Register Rn contents from Index (R2) into R0 */ tst r3, #0x10 /* B4: Immediate (0) or Register (1) shift count */ /* check bitshift op */ and r3, r3, #0x60 /* shift type */ mov r3, r3, lsr #5 /* convert into shift type jumptable index */ bne _arm_get_reg_shift /* Flags set previously via TST r3 (B4) */ _arm_calc_const_shift: movs r1, r3, lsr #7 /* Immediate shift count, 5 bit unsigned value in R1 */ bne _arm_calc_shifted_rm_val /* Non-zero shift count, process normally */ /* Must check for RRX == ROR #0 */ teq r3, #0x3 /* ROR == 0x3 */ addeq r3, r3, #1 b _arm_calc_shifted_rm_val _arm_get_reg_shift: mov r2, r3, lsr #8 /* Register-based shift count, 4 bit register enum in R2 */ _regenum2index r2, r2 /* Convert Enum into Index in R2 */ _getdbgregisterfromindex r2, r1 /* Retrieve Register value (shift count) from Index (R2) into R1 */ _arm_calc_shifted_rm_val: _dbg_jumpTableHandler debug_regShiftJumpTable, r2, r3 /* Calculate RmShifted value from R0: Rn Register val, R1: Shift/Rotate val */ ldmfd sp!, {pc} /* Rm Shifted Shift Type Jump Table Routines * On entry: * R0: Register Rm * R1: Shift/Rotate Amount * On exit: * R0: RmShifted result * R1: destroyed * */ _reg_lsl: lsl r0, r0, r1 bx lr _reg_lsr: lsr r0, r0, r1 bx lr _reg_asr: asr r0, r0, r1 bx lr _reg_ror: ror r0, r0, r1 bx lr _reg_rrx: _getdbgregister DBGSTACK_USERCPSR_INDEX, r1 /* Retrieve CPSR contents into R1 */ ands r1, r1, #CPSR_CFLAG /* Keep C Flag */ movne r1, #0x80000000 /* Set B31 if C Flag set */ lsr r0, r0, #1 /* Rm >> 1 */ orr r0, r0, r1 /* Put C flag into B31 */ bx lr /* _arm_data_instr_handler * ARM Data Processing Instruction with Rd == R15 * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) * On exit: * R0: following instruction address * R1-R7: Destroyed */ _arm_data_instr_handler: stmfd sp!, {lr} ldr r1, =ARM_DATA_INSTR_MASK and r3, r4, r1 /* Keep base instruction Opcode in R3 */ ldr r1, =ARM_DATA_INSTR_MSRMRS teq r3, r1 /* Check for MSR / MRS instruction */ _arm_is_msr_mrs_instr: moveq r0, r6 /* Copy default next instruciton address to R0 */ beq _exit_arm_data_instr_handler /* Return default next instruction address */ /* Not MSR / MRS, so process normally */ _arm_check_operand2_type: tst r4, #ARM_DATA_INSTR_IMMREG /* Check for Immediate (1) or Register (0) Operand 2 */ beq _arm_op2_is_reg _arm_op2_is_imm: and r1, r4, #BYTE0 /* 8 bit unsigned constant in R1 */ and r2, r4, #NIBBLE2 /* (rotate count / 2) in R2[11:8] */ lsr r2, r2, #7 /* actual rotate count in R2[4:0] */ ror r1, r1, r2 /* Rotated constant in R1 */ b _arm_get_operand1_val _arm_op2_is_reg: ldr r1, =(NIBBLE2|BYTE0) and r0, r4, r1 /* 12 bit register operand in R1 */ bl _arm_rmshifted_val /* R0 contains the Rm shifted val */ mov r1, r0 /* R1: Operand2 val */ _arm_get_operand1_val: bl _dbg_data_instr_retrieve_op1val /* R0: Register Rn (Operand1) val */ _arm_calc_data_instr_val: and r3, r4, #ARM_DATA_INSTR_NORMAL /* Mask Instruction Opcode into R3[24:21] */ lsr r3, r3, #21 /* Shift Data Processing Opcode into R3[3:0] */ /* Calculate data instruction value from R0: Register Rn (Operand1) val, R1: Operand2 val, R5[3:0]: CPSR, R6: Default Next Instr Addr */ _dbg_jumpTableHandler debug_dataInstrJumpTable, r2, r3 /* Next Instruction Address in R0 */ _exit_arm_data_instr_handler: ldmfd sp!, {pc} /* _dbg_data_instr_retrieve_op1val * Retrieve Data Instruction Operand 1 value * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Next Instruction Address (PC+4) * On exit: * R0: Register Rn (Operand 1) value * R2, R3: Destroyed * */ _dbg_data_instr_retrieve_op1val: and r3, r4, #NIBBLE4 /* Store Rn (Operand1) Register Enum into R3[19:16] */ lsr r3, r3, #16 /* Shift into R3[3:0] */ _regenum2index r3, r2 /* Convert Enum into Index in R2 */ _getdbgregisterfromindex r2, r0 /* Retrieve Register contents from Index (R2) into R0 */ teq r3, #REG_PC /* Check if it is PC relative */ addeq r0, r0, #8 /* R0: Register Rn (Operand1) val; adjust for PC relative (+8) */ bx lr /* Data Processing Instruction Jump Table Routines * On entry: * R0: Register Rn (Operand 1) value * R1: Operand 2 value * R5[3:0]: CPSR condition codes * R6: Default Next Instruction Address (PC+4) * On exit: * R0: Calculated result * R1, R2, R3: Destroyed * */ _opcode_and: and r0, r0, r1 bx lr _opcode_eor: eor r0, r0, r1 bx lr _opcode_sub: sub r0, r0, r1 bx lr _opcode_rsb: rsb r0, r0, r1 bx lr _opcode_add: add r0, r0, r1 bx lr _opcode_adc: /* Op1 + Op2 + C */ tst r5, #(CPSR_CFLAG>> 28) /* R5[3:0] is shifted CPSR value: Test C Flag */ add r0, r0, r1 addne r0, r0, #1 /* Add C if set */ bx lr _opcode_sbc: /* Op1 - Op2 + C - 1 */ tst r5, #(CPSR_CFLAG>> 28) /* R5[3:0] is shifted CPSR value: Test C Flag */ sub r0, r0, r1 subeq r0, r0, #1 /* If C clear, subtract 1, else (C - 1) = 0 */ bx lr _opcode_rsc: /* Op2 - Op1 + C - 1 */ tst r5, #(CPSR_CFLAG>> 28) /* R5[3:0] is shifted CPSR value: Test C Flag */ rsb r0, r0, r1 subeq r0, r0, #1 /* If C clear, subtract 1, else (C - 1) = 0 */ bx lr _opcode_tst: _opcode_teq: _opcode_cmp: _opcode_cmn: mov r0, r6 /* Next Instruction Address is not modified */ bx lr _opcode_orr: orr r0, r0, r1 bx lr _opcode_mov: mov r0, r1 /* Operand 1 is ignored */ bx lr _opcode_bic: bic r0, r0, r1 bx lr _opcode_mvn: mvn r0, r1 /* Operand 1 is ignored */ bx lr /* _arm_bx_blx_handler * BX or BLX Rm Handler. Note v4t does not have BLX instr * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1,R2: destroyed */ _arm_bx_blx_handler: stmfd sp!, {lr} and r2, r4, #NIBBLE0 /* Register Rn Enum in R2 */ _regenum2index r2, r1 /* Convert Enum into Index in R1 */ _getdbgregisterfromindex r1, r0 /* Retrieve Register contents from Index (R1) into R0 */ teq r2, #REG_PC addeq r0, r0, #8 /* Adjust PC relative register value (for BX PC) */ /* Here, the register value would have B0 set to indicate switch to Thumb mode */ ldmfd sp!, {pc} /* _arm_ldr_pc_handler * LDR with Rd = PC * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) * On exit: * R0: following instruction address * R1, R2, R3, R4, R5: destroyed */ _arm_ldr_pc_handler: stmfd sp!, {lr} mov r1, #0 /* R1: Post-Indexed Offset (cleared) */ tst r4, #ARM_LDR_INSTR_PREPOST /* Pre (1) or Post (0) Indexed */ beq _get_rn_val /* If Post-Indexed, just use Rn directly */ /* Pre-Indexed */ ldr r0, =(NIBBLE2|BYTE0) and r0, r4, r0 /* R0: 12 bit Immediate value or Shifted Reg operand */ tst r4, #ARM_LDR_INSTR_REGIMM /* Register (1) or Immediate (0) */ beq _calc_ldr_pc_offset /* Immediate value is already in R0 */ _get_shiftedreg_val: bl _arm_rmshifted_val /* Convert Rm shifted operand in R0 into value in R0 */ _calc_ldr_pc_offset: mov r1, r0 /* Keep Offset in R1 */ _get_rn_val: bl _dbg_data_instr_retrieve_op1val /* R0: Register Rn (Operand1) val */ _calc_op1val_with_offset: tst r4, #ARM_LDR_INSTR_UPDOWN /* Add (1) or Subtract (0) */ addne r0, r0, r1 /* If Add, R0 = Rn + Offset */ subeq r0, r0, r1 /* If Sub, R0 = Rn - Offset */ _get_ldr_pc_val_from_mem: ldr r0, [r0] /* Retrieve value from Memory at address given in R0 */ ldmfd sp!, {pc} /* _arm_ldm_pc_handler * LDM {pc} * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) * On exit: * R0: following instruction address * R2, R3: destroyed * * Note: The algorithm from eCos arm_stub.c does not deal with the Pre/Post-Indexed addressing (P) bit. * The algorithm here loads different content using LDM based on the value of the P bit. */ _arm_ldm_pc_handler: stmfd sp!, {lr} bl _dbg_data_instr_retrieve_op1val /* R0: Register Rn (Operand1) val */ _arm_get_regcount: mov r2, #0 /* Initialize reg_count (R2) to 0 */ mov r3, r4, lsl #16 /* Keep HLFWORD0 containing vector bits in R3[31:16] */ /* This shortens the checking to a max of 16 iterations, since the PC bit should be set */ 1: movs r3, r3, lsl #1 /* count number of '1' bits */ addcs r2, r2, #1 /* increment reg_count (R2) if C Flag set */ bne 1b /* continue until vector is empty */ /* Pre-Incr: Rn += reg_count x 4 * Post-Incr: Rn += (reg_count - 1) x 4 * Pre-Decr: Rn -= 4 * Post-Decr: Rn */ _arm_check_updown_offset: tst r4, #ARM_LDM_INSTR_UPDOWN /* Check Up (1) or Down (0) */ beq _arm_check_prepost_decr _arm_check_prepost_incr: tst r4, #ARM_LDM_INSTR_PREPOST /* Check Pre (1) or Post (0) */ subeq r2, r2, #1 /* Post-Incr: Decrement reg_count in R2 */ add r0, r0, r2, lsl #2 /* Increment Offset: Rn (R0) += reg_count (R2) x 4 */ b _get_ldm_pc_val_from_mem _arm_check_prepost_decr: tst r4, #ARM_LDM_INSTR_PREPOST /* Check Pre (1) or Post (0) */ /* Post-Decr: Rn unchanged */ subne r0, r0, #4 /* Pre-Decr: Rn (R0) -= 4 */ _get_ldm_pc_val_from_mem: ldr r0, [r0] /* Retrieve stack content for new PC value */ ldmfd sp!, {pc} /* _arm_b_bl_blx_handler * B, BL or BLX . Note v4t does not have BLX instr * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) * On exit: * R0: following instruction address * R1: destroyed */ _arm_b_bl_blx_handler: stmfd sp!, {lr} _arm_b_bl_blx_get_offset: and r0, r4, #(BYTE2|BYTE1|BYTE0) /* Encoded Branch offset in R4[23:0] */ lsl r0, r0, #(32-24) /* Shift to R0[31:8] */ asr r0, r0, #(32-26) /* Actual Signed offset = Encode Offset x 4 in R0[25:0] */ add r1, r6, #4 /* R1: (PC+4) + 4 */ add r0, r0, r1 /* Calculate Branch Target Address R0: (PC+8) + signed offset */ #ifndef __ARM6OR7__ /* armv5t or later, has BLX support */ and r1, r4, #ARM_BLX_INSTR_MASK /* Mask out Condition Code and Opcode */ teq r1, #ARM_BLX_INSTR_BLX /* Look for BLX */ bne _exit_arm_b_bl_blx_handler /* No, it is a B/BL instruction */ tst r4, #ARM_BLX_INSTR_HBIT /* H bit for Thumb Halfword Address */ orrne r0, r0, #0x02 /* Set Halfword Address R0[1] */ orr r0, r0, #BKPT_STATE_THUMB_FLAG /* Set R0[0] since BLX instr used to switch to Thumb mode */ #endif _exit_arm_b_bl_blx_handler: ldmfd sp!, {pc} /* _arm_coproc_swi_handler * SVC (SWI) or Coprocessor instruction * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+4) * On exit: * R0: following instruction address */ _arm_coproc_swi_handler: and r0, r4, #ARM_SWI_INSTR_MASK teq r0, #ARM_SWI_INSTR_VAL /* SVC (SWI) instruction */ ldreq r0, =SVC_VECTOR /* SWI: Return SVC Vector Address */ movne r0, r6 /* CoProc: Use default Following Instruction Address */ _exit_arm_coproc_swi_handler: bx lr /* _thumb_bx_blx_handler * BX or BLX Handler. Note: Link (L:b7) is not checked in the mask; armv4t does not support BLX. * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+2) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1, R2: destroyed */ _thumb_bx_blx_handler: stmfd sp!, {lr} and r2, r4, #THUMB_BLX_INSTR_REG_RNMASK /* Register Rn Enum in R2[6:3] (Hi-Reg indicated by B6) */ mov r2, r2, lsr #3 /* Shift Rn Enum to R2[3:0] */ _regenum2index r2, r1 /* Convert Enum into Index in R1 */ _getdbgregisterfromindex r1, r0 /* Retrieve Register contents from Index (R1) into R0 */ teq r2, #REG_PC addeq r0, r0, #4 /* Adjust PC relative register value (for BX PC) */ /* Here, the register value would have R0[0] set to indicate switch to Thumb mode */ ldmfd sp!, {pc} /* _thumb_poppc_handler * PUSH/POP, specifically POP {Rlist,PC} * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+2) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1-R3: destroyed */ _thumb_poppc_handler: stmfd sp!, {lr} _thumb_get_SP_val: _getdbgregister DBGSTACK_USERSP_INDEX, r1 /* Retrieve SP contents into R1 */ _thumb_get_regcount: mov r3, r4, lsl #24 /* Keep BYTE0 containing vector bits in R3[31:24] */ /* POP is equivalent to LDMFD. Load PC is encoded in b8, * the 8-bit vector is for Lo registers. * This shortens the checking to a max of 8 iterations */ 1: movs r3, r3, lsl #1 /* count number of '1' bits */ addcs r1, r1, #4 /* Walk the stack to locate the PUSHed LR (POP PC) value */ bne 1b /* continue until vector is empty */ ldr r0, [r1] /* Retrieve new PC value */ #if 0 /* PC Value should have B0 set */ orr r0, r0, #BKPT_STATE_THUMB_FLAG /* Force R0[0] since it is used to indicates Thumb mode */ #endif ldmfd sp!, {pc} /* _thumb_bcond_swi_handler * B or SWI (SVC) * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+2) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1-R3: destroyed */ _thumb_bcond_swi_handler: stmfd sp!, {lr} and r2, r4, #THUMB_BCOND_SWI_INSTR_CONDMASK /* Keep Condition Code R2[11:8] */ teq r2, #THUMB_BCOND_SWI_INSTR_SWI /* SVC (SWI) instruction */ _thumb_swi_instr: ldreq r0, =SVC_VECTOR /* Return SVC Vector Address */ beq _exit_thumb_bcond_swi_handler /* Switch to ARM mode for SVC */ _thum_bcond_unused_instr: teq r2, #THUMB_BCOND_SWI_COND_UNUSED moveq r0, r6 /* False (don't execute), so use Default Following Instruction Address */ beq _exit_thumb_bcond_instr _thumb_bcond_instr: stmfd sp!, {r4} /* Preserve Opcode in R4 */ lsl r4, r2, #(32-12) /* Shift condition code in R2[11:8] to R0[31:28] to match ARM cond-code format */ bl _dbg_check_arm_condcode /* Use ARM condition code checking routine to test (R4, R6 unchanged) */ ldmfd sp!, {r4} /* Restore Opcode in R4 */ teq r0, #FALSE moveq r0, r6 /* False (don't execute), so use Default Following Instruction Address */ beq _exit_thumb_bcond_instr _thumb_calc_bcond_offset: lsl r0, r4, #(32-8) /* Shift 8-bit offset in R4[7:0] to R0[31:24] */ asr r0, r0, #(32-9) /* Convert into 9-bit signed offset in R0[8:0] */ add r0, r6, r0 /* PC+2 + signed offset */ add r0, r0, #2 /* PC+4 + signed offset */ _exit_thumb_bcond_instr: orr r0, r0, #BKPT_STATE_THUMB_FLAG /* Set R0[0] since it is used to indicates Thumb mode */ _exit_thumb_bcond_swi_handler: ldmfd sp!, {pc} /* _thumb_b_handler * B * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+2) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1: destroyed * Note: The signed offset is 12-bits (encoded value x 2) */ _thumb_b_handler: stmfd sp!, {lr} lsl r0, r4, #(32-11) /* Shift 11-bit offset in R4[10:0] to R0[31:21] */ asr r0, r0, #(32-12) /* Convert into 12-bit signed offset in R0[11:0] */ add r0, r6, r0 /* PC+2 + signed offset */ add r0, r0, #2 /* PC+4 + signed offset */ orr r0, r0, #BKPT_STATE_THUMB_FLAG /* Set R0[0] since it is used to indicates Thumb mode */ ldmfd sp!, {pc} /* _thumb_long_bl_blx_handler * Long BL or BLX (4 bytes) Note: b11 (H) indicates 1st or 2nd instr; armv4t does not support BLX. * On entry: * R4: Opcode of instruction to be executed * R5[3:0]: CPSR condition codes * R6: Default Following Instruction Address (PC+2) * On exit: * R0: following instruction address (B0 set to indicate Thumb mode) * R1, R2, R3: destroyed * R6: Subseqent Instruction Address (PC+4) if first instruction is valid, else unchanged (PC+2) * Note: The BL instruction (0xFxxx) should be in pairs (Dual 16-bit instructions). * The first instruction should have (H=0) to indicate the upper 11 bits of the encoded offset * The second instruction should have (H=1) to indicate the lower 11 bits of the encoded offset * The signed offset is 23 bits (encoded value x 2) * * Note2: The BLX instruction (0xExxx) encodes the first instruciton using BL (0xFxxx) with H=0, * while the second instruction has a different opcode value (0xExxx), with H=1. * BLX is only used to switch to an ARM target. */ _thumb_long_bl_blx_handler: stmfd sp!, {lr} _thumb_check_1st_bl_blx_instruction: tst r4, #THUMB_BLX_INSTR_IMM_HBIT /* Check H bit */ bne _return_default_thumb_following_instr /* H=1 as first instruction shouldn't happen */ _thumb_check_2nd_bl_blx_instruction: ldrh r3, [r6] /* Get second instruction in pair at PC+2 into R3 */ add r6, r6, #2 /* Skip to Subsequent Instruction (PC+4) */ tst r3, #THUMB_BLX_INSTR_IMM_HBIT /* Check H bit */ beq _return_default_thumb_following_instr /* H=0 as second instruction shouldn't happen */ _thumb_concat_branch_offset: lsl r0, r4, #(32-11) /* Shift first instruction 11-bit offset in R4[10:0] to R0[31:21] */ asr r0, r0, #(32-23) /* Convert into 12-bit signed offset in R0[22:12] */ lsl r2, r3, #(32-11) /* Shift second instruction 11-bit offset in R3[10:0] to R2[31:21] */ lsr r2, r2, #(32-12) /* Convert into 12-bit unsigned offset in R2[11:0] */ orr r0, r0, r2 /* Combine offsets */ add r0, r6, r0 /* PC+4 + signed offset */ _thumb_check_bl_blx_pair: and r3, r3, #THUMB_BLX_INSTR_IMM_MASK /* Keep second instruction opcode in R3 */ teq r3, #THUMB_BLX_INSTR_IMM_BL /* Look for BL */ beq _flag_thumb_instr_addr /* Return BL target address in R0 */ #ifndef __ARM6OR7__ /* v5t or higher architecture */ teq r3, #THUMB_BLX_INSTR_IMM_BLX /* Look for BLX */ biceq r0, r0, #0x03 /* Match, Force ARM address */ beq _exit_thumb_long_bl_blx_handler #endif _return_default_thumb_following_instr: /* FIXME: This assumes that once the 4-byte sequence check fails, it will return PC+4, * regardless of whether the second instruction is a valid BL/BLX instruction or not. */ mov r0, r6 /* Return default Following/Subsequent Instruction Address */ _flag_thumb_instr_addr: orr r0, r0, #BKPT_STATE_THUMB_FLAG /* Set R0[0] since it is used to indicates Thumb mode */ _exit_thumb_long_bl_blx_handler: ldmfd sp!, {pc} nxt-firmware-1.29.7/armdebug/Debugger/debug_runlooptasks.S000066400000000000000000000354551466344546000236350ustar00rootroot00000000000000/** @file debug_runlooptasks.S * @brief GDB Server platform Run Loop * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ /* * This file contains platform specific code. * This include ABORT Mode Debugger Run Loop operation, * as well as Debugger Interfacing code to the platform code. */ /* * The Debugger has to implement a Run Loop in ABORT mode * since the hardware is still running. Consequently, * the communications subsystems such as USB (and Bluetooth?) * which is used to communicate with the Host needs to be * serviced in order for actual data transmission and reception * to take place. Currently we're reusing the platform's * communication routines to do the actual tx/rx, so it means * that it is not possible to set breakpoints in those modules. * In addition, since the platform communication modules may * handle other tasks, it is currently possible to enter an * indeterminate state where certain communication messages trigger * a platform response which cannot be handled by the Debugger Run Loop. * The alternative is to implement our own communications routines, but * that will take even more code. * * FIXME: It may become necessary to hack the platform communications * routines to detect that we're in the Debugger Run Loop and not the * normal run loop to avoid system crashes, but the current goal is to * have as minimal changes to the platform code as possible. * * Since there are two Run Loops for the platform, the way in which * they interact is as follows: * * [Platform Run Loop] - DBG_INIT/ GDB Cmd/ BKPT -> [Debugger Run Loop] * \ <------ GO/ STEP/ CONT ----- / * ... ... * ... Handle GDB Cmd/Resp * ... ... * {normal runloop {communications / * processing} watchdog routines} * ^-------v v-------^ * * The Platform will invoke dbg__bkpt_init() after hardware and system initialization, * before entering the Platform Run Loop. This configures the Debugger, but does not * invoke the Debugger Run Loop unless a Manual Breakpoint is found in the platform code. * * Subsequently, the Debugger Run Loop will be triggered by Breakpoints, or * when the communications subsystem receives a GDB Command. * * The Debugger Run Loop is actually dbg__bkpt_waitCMD(), this file contains * the Run Loop Tasks which needs to be invoked periodically by the Run Loop, * to minimize the coupling between the ARMDEBUG modules and the Platform. * * Note: The Debugger Run Loop does not handle Hardware Shutdown, it is * assumed that we wouldn't need to do so in Debug Mode. * */ #define __ASSEMBLY__ #define REBOOT_POWERDOWN #include "debug_runlooptasks.h" #include "debug_internals.h" #include "debug_macros.h" #include "debug_stub.h" .code 32 .align 4 #ifdef __NXOS__ /**************************************************************************** * * NxOS Run Loop * ****************************************************************************/ dbg_interwork dbg__runloopTasks /* Currently, there's nothing that needs to be done in the NxOS Run Loop */ push {lr} mov r0, #1 /* 1 ms delay */ bl nx_systick_wait_ms pop {pc} #else /**************************************************************************** * * NXT Firmware Run Loop * ****************************************************************************/ dbg_interwork dbg__runloopTasks push {lr} /* FIXME: Add necessary cXXXCtrl calls here */ bl cCommCtrl /* OSWatchdogWrite is a NULL function in the NXT Firmware?! */ pop {pc} #endif #ifdef __NXOS__ /**************************************************************************** * * NxOS Reboot Routine * ****************************************************************************/ dbg_interwork dbg__reboot #ifdef REBOOT_POWERDOWN b nx_core_halt /* Shutdown Brick, won't return */ #else b nx_core_reset /* Reboot Brick, won't return */ #endif #else /**************************************************************************** * * NXT Firmware Reboot Routine * ****************************************************************************/ dbg_interwork dbg__reboot /* Powerdown Sequence dIOCtrlSetPower((POWERDOWN>>8)); dIOCtrlTransfer(); Reboot Sequence dIOCtrlSetPower((UBYTE)(BOOT>>8)); dIOCtrlSetPwm((UBYTE)BOOT); dIOCtrlTransfer(); */ /* We implement the powerdown sequence for now */ #ifdef REBOOT_POWERDOWN /* Powerdown sequence */ ldr r0, =((POWERDOWN >> 8) & 0xFF) ldr r1, =dIOCtrlSetPower mov lr,pc bx r1 #else /* Reboot sequence: this forces SAMBA mode??!! */ ldr r0, =((BOOT >> 8) & 0xFF) ldr r1, =dIOCtrlSetPower mov lr,pc bx r1 ldr r0, =(BOOT & 0xFF) ldr r1, =dIOCtrlSetPwm mov lr,pc bx r1 #endif _dbg__reboot_wait: ldr r1, =dIOCtrlTransfer mov lr,pc bx r1 b _dbg__reboot_wait /* Wait for AVR... */ #endif #ifdef __NXOS__ /**************************************************************************** * * NxOS Abort Info LCD Display Routine * ****************************************************************************/ /* On entry: * r0: abort type * On exit: * r0-r3: destroyed */ dbg_interwork dbg__display_abort_info push {lr} _getdbgregister DBGSTACK_USERPC_INDEX, r1 /* Retrieve User PC into R2 */ _getdbgregister DBGSTACK_USERCPSR_INDEX, r2 /* Retrieve User CPSR into R2 */ bl nx__abort_info /* void nx__abort_info(U32 data, U32 pc, U32 cpsr); */ pop {pc} #else /**************************************************************************** * * NXT Firmware Abort Info LCD Display Routine * ****************************************************************************/ dbg_interwork dbg__display_abort_info /* FIXME: Inteface with NXT Firmware LCD Display routines */ push {lr} pop {pc} #endif #ifdef __NXOS__ .extern debug_OutCommBuf /**************************************************************************** * * NxOS Communications Driver Interface Routine * ****************************************************************************/ /* dbg__sendCommMsg * Internal send routine (interfaces with drivers). * On entry: * R0: Total Message Buffer length * On exit: * R0: Tx Status (TRUE if data sent) * R1-R3: Destroyed */ dbg_interwork dbg__sendCommMsg stmfd sp!, {r4, lr} mov r4, r0 /* Keep Comm Buffer length in R4 */ /* Check USB bus status, transmit message if possible */ bl nx_usb_is_connected /* R0 = TRUE (#1) if USB is ready */ teq r0, #0 /* FALSE == #0; We can't check for True condition since values used by C-Compiler & ARMDEBUG are different */ beq dbg__sendCommMsgFailed /* Actual transmission (blocking) */ ldr r0, =debug_OutCommBuf /* data pointer parameter */ mov r1, r4 /* Comm buffer length */ bl nx_usb_write 1: bl nx_usb_data_written /* R0 = True if data has been sent */ teq r0, #0 /* FALSE == #0; We can't check for True condition since values used by C-Compiler & ARMDEBUG are different */ /* FIXME: implement timeout */ beq 1b /* Busy Wait Loop */ mov r0, #TRUE b exit_dbg__sendCommMsg dbg__sendCommMsgFailed: mov r0, #FALSE exit_dbg__sendCommMsg: ldmfd sp!, {r4, pc} #else /**************************************************************************** * * NXT Firmware Communications Driver Interface Routine * ****************************************************************************/ /* dbg__sendCommMsg * Internal send routine (interfaces with drivers). * On entry: * R0: Total Message Buffer length * On exit: R0: Tx Status (TRUE if data sent) */ dbg_interwork dbg__sendCommMsg stmfd sp!, {r4, lr} mov r4, r0 /* Keep Comm Buffer length in R4 */ ldr r0, =debug_nxtCommChannel ldr r0, [r0] /* Get Channel Enum */ teq r0, #BT_CMD_READY beq dbg__sendBTMsg teq r0, #USB_CMD_READY beq dbg__sendUSBMsg b dbg__sendCommMsgFailed /* Channel Enum Doesn't Match, shouldn't happen? */ dbg__sendBTMsg: /* NXT BT routines do not have any configuration checks */ ldr r0, =debug_OutCommBuf /* data pointer parameter */ mov r1, r4 /* BT Bytes to Send */ mov r2, r4 /* BT Message Size */ bl dBtSendMsg /* Send it via Bluetooth (complete message) */ mov r0, #TRUE /* Always flag Success */ b exit_dbg__sendCommMsg dbg__sendUSBMsg: /* Check USB bus status, transmit message if possible */ bl dUsbIsConfigured /* R0: UByte status, TRUE / FALSE */ teq r0, #nxt_UBYTE_TRUE bne dbg__sendCommMsgFailed /* Actual transmission (blocking) */ ldr r0, =debug_OutCommBuf /* data pointer parameter */ mov r1, r4 /* Comm buffer length */ bl dUsbWrite /* call NXT Firmware USB driver, return 0: done, !0: remaining chars */ teq r0, #0 /* Tx done if returned length is 0 */ moveq r0, #TRUE /* Convert NXT firmware return value to our Status (TRUE/FALSE) */ beq exit_dbg__sendCommMsg dbg__sendCommMsgFailed: mov r0, #FALSE exit_dbg__sendCommMsg: ldmfd sp!, {r4, pc} #endif #ifdef __NXOS__ /**************************************************************************** * * GDB Debugger Invocation Routine for NxOS * ****************************************************************************/ .code 32 .align 4 .extern dbg__install_singlestep .extern dbg__activate_singlestep .extern irq_stack_frame_address /* nxos__handleDebug * Prepare to switch to Debug Mode * int nxos__handleDebug(U8 *buffer, comm_chan_t channel, U32 length); * * This routine is called from NxOS Fantom library to setup * Single Step Breakpoint in preparation for Debugger invocation if we're in * normal execution mode. * * It returns to complete the IRQ handling normally, after which the single * step breakpoint will be triggered, and the incoming GDB message will then * be processed in the dbg__bkpt_waitCMD() loop. * * If we're in Debugger Mode already, then just return and let the * dbg__bkpt_waitCMD() loop handle it normally. * * If we're operating in normal NxOS mode, return True (!0) * If we're already in Debugger Mode, return False (0) */ dbg_interwork nxos__handleDebug push {lr} /* This routine is called from nx__irq_handler() via fantom_filter_packet(). * The operating mode should already have been configured by the IRQ interrupt handler. * * The IRQ Stack Frame Pointer will contains the LR and SPSR from the topmost interrupted task * if it is non-zero (NxOS supports nested IRQs) */ bl dbg__copyNxtDebugMsg /* Copy to Debugger Message Buffer, Remember Comm Channel */ mov r0, #FALSE /* Setup Default Return value (False) */ _dbg_getmode r1 /* Get Debug Mode */ cmp r1, #(TRUE & BYTE0) /* Confine it to Byte size */ /* If Debug Mode is TRUE, this means that we're already running the Debugger */ beq exit_nxos__handleDebug /* Yes, return False */ /* Retrieve ISR Return Address */ ldr r3, =irq_stack_frame_address ldr r3, [r3] /* Get Interrupt Stack Pointer */ teq r3, #0 beq exit_nxos__handleDebug /* NULL Interrupt Stack Frame Pointer, exit (status: False) */ nxos_switch2debug: /* Since the Interrupt Stack Frame Pointer points to the top of the stack frame, * we'll have to use Load Empty Ascending Stack (LDMEA == LDMDB) to access the variables */ ldmdb r3, {r1,r2} /* R1: LR, R2: SPSR */ tst r2, #CPSR_THUMB /* Check for Thumb Mode */ orrne r1, r1, #1 /* Configure for Thumb Single Step Breakpoint */ bl dbg__install_singlestep /* Setup Single Step, next instruction address returned in r1 */ bl dbg__activate_singlestep mov r0, #TRUE /* We're going to switch to Debug Mode (via Single Step Breakpoint) */ exit_nxos__handleDebug: pop {r1} bx r1 /* In case we have Interworking from different caller mode */ #else /**************************************************************************** * * GDB Debugger Invocation Routine for NXT Firmware * ****************************************************************************/ .code 16 .align 2 .extern dbg__copyNxtDebugMsg .global cCommHandleDebug .thumb_func .type cCommHandleDebug, %function /* cCommHandleDebug * Switch Mode to Debugger. * Used by NXT Firmware only * * UWORD cCommHandleDebug(UBYTE *pInBuf, UBYTE CmdBit, UWORD MsgLength); * * This routine is called from cCommInterprete either in normal operation mode (SVC) * or else when we're in debug mode (ABORT) which uses the cCommCtrl() routine to handle * I/O with the Host. * * On entry, the message is copied from the NXT buffer into our own buffers. * * If this is accessed from normal operation mode, we need to switch mode to * ABORT mode to handle the incoming message using a Manual Breakpoint instruction. * When DEBUG is exited, the execution resumes from the instruction following the Breakpoint. */ cCommHandleDebug: /* Arg Registers are not preserved since this is invoked explicitly */ push {lr} /* store arg registers */ bl dbg__copyNxtDebugMsg /* Copy to Debugger Message Buffer, Remember Comm Channel */ _dbg_getmode r0 /* Get Debug Mode */ cmp r0, #(TRUE & BYTE0) /* Confine it to Byte size */ /* If Debug Mode is TRUE, this means that we're already running the Debugger */ beq _cCommHandleDebug_cont /* Else, we're in normal operation mode (SVC), or other mode (??!) and need to force a switch to Debug mode */ dbg__bkpt_thumb _cCommHandleDebug_cont: mov r0, #0 /* FIXME: Return Status */ pop {r1} /* Can't Pop LR directly */ bx r1 /* Safe code: actually we should be able to Pop PC since the caller is Thumb Mode */ .ltorg #endif nxt-firmware-1.29.7/armdebug/Debugger/debug_runlooptasks.h000066400000000000000000000035401466344546000236500ustar00rootroot00000000000000/** @file debug_runlooptasks.h * @brief Shared C/ASM header file for debugger communications * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #ifndef __DEBUG_RUNLOOPTASKS_H__ #define __DEBUG_RUNLOOPTASKS_H__ #include "_c_arm_macros.h" /* This is a place holder header file to allow for interfacing with C Routines in either * NxOS or NXT Firmware. * * Since the header files from the original source trees were meant for C programs, we can't * include them directly. Here we just use .extern to reference the routines. */ #ifdef __NXOS__ .extern nx__abort_info .extern nx_systick_wait_ms .extern nx_usb_is_connected .extern nx_usb_can_write .extern nx_usb_write .extern nx_usb_data_written .extern nx_usb_read .extern nx_usb_data_read .extern nx_core_reset .extern nx_core_halt #else /* NXT Firmware */ .extern cCommInit .extern cCommCtrl .extern cCommExit .extern dUsbWrite .extern dUsbRead .extern dUsbIsConfigured .extern dBtSendMsg /** * True value used by Thumb mode in NXT */ .equ nxt_UBYTE_TRUE, 1 /** * False value used by Thumb mode in NXT */ .equ nxt_UBYTE_FALSE, 0 /** * USB Command Indicator */ .equ USB_CMD_READY, 0x01 /* From c_comm.iom */ /** * BT Command Indicator */ .equ BT_CMD_READY, 0x02 /* From c_comm.iom */ .extern dIOCtrlSetPower .extern dIOCtrlSetPwm .extern dIOCtrlTransfer /** * NXT Boot Magic Value */ .equ BOOT, 0xA55A /* from c_ioctrl.iom */ /** * NXT Powerdown Magic Value */ .equ POWERDOWN, 0x5A00 /* from c_ioctrl.iom */ #endif #endif nxt-firmware-1.29.7/armdebug/Debugger/debug_stack.ld000066400000000000000000000010271466344546000223570ustar00rootroot00000000000000/* The following linker definitions should be placed in the stack section */ /* debugger state */ __debugger_stack_bottom__ = . ; . += 0x48; /* 16 previous mode registers + SPSR + UNDEF Next Instruction Address */ __debugger_stack__ = .; __debugger_stack_top__ = . ; /* breakpoints */ __breakpoints_start__ = . ; . += 0x40; /* Single Stepping Breakpoint + 7 Breakpoints */ __breakpoints_end__ = . ; /* Symbols */ __breakpoints_num__ = (__breakpoints_end__ - __breakpoints_start__) / 8; nxt-firmware-1.29.7/armdebug/Debugger/debug_stub.S000066400000000000000000001633651466344546000220500ustar00rootroot00000000000000/** @file debug_stub.S * @brief ARM Breakpoint Debugger support routines * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ /* GDB sparc-stub.c comments header included below to document GDB Server Remote protocol */ /* This header has been modified to include additional commands not documented in the header stub */ /**************************************************************************** THIS SOFTWARE IS NOT COPYRIGHTED HP offers the following for use in the public domain. HP makes no warranty with regard to the software or it's performance and the user accepts the software "AS IS" with all faults. HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ****************************************************************************/ /**************************************************************************** * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ * * Module name: remcom.c $ * Revision: 1.34 $ * Date: 91/03/09 12:29:49 $ * Contributor: Lake Stevens Instrument Division$ * * Description: low level support for gdb debugger. $ * * Considerations: only works on target hardware $ * * Written by: Glenn Engel $ * ModuleState: Experimental $ * * NOTES: See Below $ * * Modified for SPARC by Stu Grossman, Cygnus Support. * * This code has been extensively tested on the Fujitsu SPARClite demo board. * * To enable debugger support, two things need to happen. One, a * call to set_debug_traps() is necessary in order to allow any breakpoints * or error conditions to be properly intercepted and reported to gdb. * Two, a breakpoint needs to be generated to begin communication. This * is most easily accomplished by a call to breakpoint(). Breakpoint() * simulates a breakpoint by executing a trap #1. * ************* * * The following gdb commands are supported: * * command function Return value * * g return the value of the CPU registers hex data or ENN * GrrrrRRRR.. set the value of the CPU registers OK or ENN * where register values are given as * 32-bit hex values in the sequence: * User R0, R1, ..., R15, CPSR * px get the value of one register (x) hex data or ENN * Px=rrrr set the value of one register (x) to OK or ENN * 32-bit hex value rrrr. * x = ['0','F'] for R0-R15, ['10','17'] for F0-F7 (dummy) * '18' for FPSCR (dummy), '19' for User CPSR * * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN * MAA..AA,LLLL:bb..bb * Write LLLL bytes at address AA.AA OK or ENN * * D Detach (equivalent to continue Ack Only * at current address) * c Resume at current address SNN ( signal NN) * cAA..AA Continue at address AA..AA SNN * * s Step one instruction SNN * sAA..AA Step one instruction from AA..AA SNN * * k kill * * ? What was the last sigval ? SNN (signal NN) * * zt,AA..AA,k Remove a Breakpoint of type t at addr OK or ENN * AA..AA of kind k * Zt,AA..AA,k Insert a Breakpoint of type t at addr OK or ENN * AA..AA of kind k * t 0: memory breakpoint * 1: hardware breakpoint * 2: write watchpoint * 3: read watchpoint * 4: access watchpoint * k: 2 (16-bit Thumb), 3 (32-bit Thumb2) * or 4 (32-bit ARM) for t=[0,1] * Num. bytes to watch for t=[2,4] * * All commands and responses are sent with a packet which includes a * checksum. A packet consists of * * $#. * * where * :: * :: < two hex digits computed as modulo 256 sum of > * * When a packet is received, it is first acknowledged with either '+' or '-'. * '+' indicates a successful transfer. '-' indicates a failed transfer. * * Example: * * Host: Reply: * $m0,10#2a +$00010203040506070809101112131415#42 * ****************************************************************************/ /* Modified GDB Server Remote Protocol definition from GDB's sparc-stub.c Comment Header included above * Additional commands from GDB Reference Appendix D.2 * * Note: ARMDEBUG can only implement Memory Breakpoints t=0. Hardware breakpoints requires a JTAG debugger. * Currently, watchpoints are not implemented as they require hardware support as well (need verification). * * GDB requires command parameters to be specified as Big Endian values. * However, the read/write register command expect the register contents to be specified in target byte order. * The default target byte order is Little Endian for the AT91SAM7xxx processor in the NXT. * If Big Endian target byte order is required, the __BIG_ENDIAN__ preprocessor label should be defined. */ /* FIXME: The Hex value arguments passed by GDB does not have fixed lengths! Although the standard says * there should be x digits, it does not follow this requirement. e.g., register index. */ #define __ASSEMBLY__ #include "debug_stub.h" #include "debug_internals.h" #include "debug_macros.h" /* Opcode Parser function reference */ .extern dbg_following_instruction_addr /* Hexutils function references */ .extern hex2char .extern char2hex .extern byte2ascii .extern halfword2ascii_be .extern halfword2ascii_le .extern word2ascii_be .extern word2ascii_le .extern ascii2hex_varlen_be .extern ascii2byte .extern ascii2halfword_be .extern ascii2halfword_le .extern ascii2word_be .extern ascii2word_le /* Macro definitions */ /* _check_msgseparator * Look for separator ',' * On entry: * bufferptr: points to the parameter buffer [can't be R0] * On exit: * R0: destroyed * bufferptr: points to the next character location in the parameter buffer * Flags: Updated */ .macro _check_msgseparator bufferptr ldrb r0, [\bufferptr], #1 /* get separator */ cmp r0, #MSGBUF_SEPCHAR .endm /* _check_msgargument * Look for argument ':' * On entry: * bufferptr: points to the parameter buffer [can't be R0] * On exit: * R0: destroyed * bufferptr: points to the next character location in the parameter buffer * Flags: Updated */ .macro _check_msgargument bufferptr ldrb r0, [\bufferptr], #1 /* get separator */ cmp r0, #MSGBUF_ARGCHAR .endm /* _check_msgassignment * Look for assignment '=' * On entry: * bufferptr: points to the parameter buffer [can't be R0] * On exit: * R0: destroyed * bufferptr: points to the next character location in the parameter buffer * Flags: Updated */ .macro _check_msgassignment bufferptr ldrb r0, [\bufferptr], #1 /* get separator */ cmp r0, #MSGBUF_SETCHAR .endm .bss .align 4 debug_InMsgBuf: .space MSGBUF_SIZE,0 debug_OutMsgBuf: .space MSGBUF_SIZE,0 /* Make Debugger State accessible from other modules */ .global debug_state .global debug_mode .global debug_bkpt_type .global debug_curr_breakpoint debug_state: .byte 0x0 /* dbg_state_t variable */ debug_mode: .byte 0x0 /* Boolean variable */ debug_bkpt_type: .byte 0x0 /* bkpt_type_t variable */ debug_curr_breakpoint: .byte 0x0 .data .align 4 debug_RetransmitFlag: .byte '-',0 debug_AckOnlyFlag: .byte '+',0 debug_ValidResponsePrefix: .byte '+','$',0 debug_ErrorResponsePrefix: .byte '+','$','E',0 debug_SignalResponsePrefix: .byte '+','$','S',0 debug_OkResponse: .byte '+','$','O','K',0 debug_ThreadIDResponse: .byte '+','$','Q','C','0',0 /* 0: Any thread */ debug_FirstThreadInfoResponse: .byte '+','$','m','0',0 /* 0: One default thread */ debug_SubsequentThreadInfoResponse: .byte '+','$','l',0 /* End of Thread List */ /* The CmdIndexTable and CmdJumpTable must be kept in sync */ debug_cmdIndexTable: .byte 'g','G','p','P','m','M','D','c','s','k','z','Z','?','q','Q',0 /* Command Handlers * On entry: * R0: Input Message Parameter Buffer address pointer (points to contents after '$' and '') */ .align 4 debug_cmdJumpTable: .word _dbg__cmd_GetAllRegs /* 'g' */ .word _dbg__cmd_SetAllRegs /* 'G' */ .word _dbg__cmd_GetOneReg /* 'p' */ .word _dbg__cmd_SetOneReg /* 'P' */ .word _dbg__cmd_ReadMem /* 'm' */ .word _dbg__cmd_WriteMem /* 'M' */ .word _dbg__cmd_Detach /* 'D' */ .word _dbg__cmd_Continue /* 'c' */ #ifdef __NXOS__ .word _dbg__cmd_Step /* 's' */ #else /* NXT Firmware does not support Stepping */ .word _dbg__nop /* 's' */ #endif .word _dbg__cmd_Kill /* 'k' */ .word _dbg__cmd_RemoveBreakpoint /* 'z' */ .word _dbg__cmd_InsertBreakpoint /* 'Z' */ .word _dbg__cmd_Status /* '?' */ .word _dbg__cmd_Query /* 'q' */ .word _dbg__nop /* 'Q' */ .word 0 .code 32 .text .align 4 .extern __breakpoints_num__ .extern dbg__getDebugMsg /* Read a message from the communications link */ .extern dbg__putDebugMsg /* Write a message to the communications link */ .extern dbg__sendAckOrNak /* Send Ack or Nak to indicate success/failure of message receipt */ .extern dbg__runloopTasks /* Platform specific Run Loop processing */ /* The Debugger Interface can handle a total of (n-1) Breakpoint States and 1 Single Stepping State, * where n is a power of 2. The value of n is given by __breakpoints_num__ defined in the linker file. * * In addition, a Debugger Stack contains the User Mode Register Stack Frame + SPSR + Bkpt Instr Addr. * These are currently stored in the .stack area in RAM, so there is no fixed address * location that is used for this purpose. * * The Breakpoint feature assumes that the program is executed in RAM. It is not possible * to set dynamic breakpoints for programs executed from Flash in the AT91SAM7S which lacks * instruction breakpointing support in hardware without using JTAG. The only type of breakpoints * that can be supported in Flash based programs are Static (predefined) breakpoints inserted into * the code. * * Each Breakpoint State i is a struct comprising the Breakpoint Address + Memory Contents * stored in 8 bytes as: * [High Memory Address] * ADDR [i*8+4]: Memory Contents (32 bits) * ADDR [i*8]: Breakpoint Address (31 bits, b0 = THUMB flag [not implemented yet]) * [Low Memory Address] * * A Non-zero Breakpoint Address means that the breakpoint is active, whereas the memory contents * contains the instruction which resided at that address initially (now replaced by a BKPT * instruction). * Note: Currently it is not possible to resume execution of a program with breakpoints enabled * after a RESET, since the RESET will clear all contents of the stack, destroying the instruction * contained in a given breakpoint. * Fortunately the NXT will also need to reload the program into RAM so this is not expected to be * an issue. * * The Memory Map for the Debugger State is as follows: * * [High Memory Address] __breakpoints_end__ * Breakpoint 07 State * Breakpoint 06 State * ... * Breakpoint 02 State * Breakpoint 01 State * Single Step State __debugger_stack__ / __breakpoints_start__ * Previous Mode R15 * Previous Mode R14 * Previous Mode R13 * ... * User Mode R02 * User Mode R01 * User Mode R00 * Previous Mode CPSR (UNDEF SPSR) * UNDEF Next Instr Addr __debugger_stack_bottom__ * [Low Memory Address] * * Each Breakpoint State will initially be zeroed. * */ /* FIXME: The Debugger Stack Frame is probably not 100% consistent with the order that GDB expects in the g/G messages. CSPR is probably located above R15 */ /**************************************************************************** * * GDB Debugger Init and Breakpoint Handler Routines * ****************************************************************************/ .code 32 .align 4 /* dbg__bkpt_init * GDB set_debug_traps() routine * On entry: * None * On exit: * r0-r3: destroyed */ dbg_interwork dbg__bkpt_init push {lr} bl _dbg__clear_breakpoints mov r2, #0 ldr r1, =debug_curr_breakpoint strb r2, [r1] ldr r0, =debug_InMsgBuf strb r2, [r0] ldr r1, =debug_OutMsgBuf strb r2, [r1] bl dbg__comm_init /* Pass R0: Rx Buffer, R1: Tx Buffer to comm submodule */ /* FIXME: Initialize other stuff here */ _dbg_setstate DBG_INIT _dbg_setmode FALSE /* Debug Mode = False */ pop {lr} bx lr /* Must return via BX; may have been called from Thumb mode (NXT Firmware) */ /* _dbg__flush_icache * Flush the Instruction cache * Defined by GDB Stub, but not needed for ARMv4T architecture */ _dbg__flush_icache: /* nop */ bx lr /* _dbg__switch2undefmode * Common internal routine to return execution to user program */ _dbg__switch2undefmode: bl _dbg__flush_icache msr cpsr_c, #(MODE_UND | CPSR_FIQ | CPSR_IRQ) /* Configure Undef Mode */ _dbg_setmode FALSE /* Debug Mode = False */ ldr lr, =resume_execution mov pc, lr /* Exit via UNDEF mode */ /* dbg__abort_exception_handler * Handle Abort Exceptions * On entry: * r0: Abort Type Enum * On exit: * routine does not 'exit' in the normal sense */ dbg_interwork dbg__abort_exception_handler _dbg_set_bkpt_type r0 /* Set Breakpoint Type given value in R0 */ b dbg__bkpt_waitCMD /* dbg__thumb_bkpt_handler * GDB handle_exception() routine (Thumb Mode) * On entry: * r0: Breakpoint index value * On exit: * routine does not 'exit' in the normal sense */ dbg_interwork dbg__thumb_bkpt_handler /* On entry, r0 contains breakpoint index value */ _dbg_setcurrbkpt_index r0 /* keep current breakpoint index in memory */ ldr r1, =BKPT16_MANUAL_BKPT teq r0, r1 moveq r0, #DBG_MANUAL_BKPT_THUMB /* Manual Thumb Breakpoint, process it */ beq _restore_normal_breakpoints _process_normal_breakpoint_thumb: ldr r1, =__breakpoints_num__ cmp r0, r1 /* Sanity check that index is in range */ bhs dbg__bkpt_offset_outofrange /* Exceeded Offset Range */ mov r0, #DBG_NORMAL_BKPT_THUMB /* It is a valid normal breakpoint */ b _restore_normal_breakpoints /* dbg__arm_bkpt_handler * GDB handle_exception() routine (ARM Mode) * On entry: * r0: breakpoint index value * On exit: * routine does not 'exit' in the normal sense */ dbg_interwork dbg__arm_bkpt_handler /* On entry, r0 contains breakpoint index value */ _dbg_setcurrbkpt_index r0 /* keep current breakpoint index in memory */ ldr r1, =BKPT32_MANUAL_BKPT teq r0, r1 moveq r0, #DBG_MANUAL_BKPT_ARM /* Manual ARM Breakpoint, process it */ beq _restore_normal_breakpoints _process_normal_breakpoint_arm: ldr r1, =__breakpoints_num__ cmp r0, r1 /* Sanity check that index is in range */ bhs dbg__bkpt_offset_outofrange /* Exceeded Offset Range */ mov r0, #DBG_NORMAL_BKPT_ARM /* It is a valid normal breakpoint */ /* b _restore_normal_breakpoints */ _restore_normal_breakpoints: _dbg_set_bkpt_type r0 /* Set Breakpoint Type given value in R0 */ bl _dbg__restore_breakpoints /* includes restoring single step */ bl _dbg__clear_singlestep bl _dbg__flush_icache /* b dbg__bkpt_waitCMD */ dbg__bkpt_inactive: /* b dbg__bkpt_waitCMD */ dbg__bkpt_offset_outofrange: /* b dbg__bkpt_waitCMD */ /* dbg__bkpt_waitCMD * GDB Stub Remote Command Handler */ /**************************************************************************** * * GDB Server Command Processing Routines * ****************************************************************************/ dbg_interwork dbg__bkpt_waitCMD /* We enter this code section when a Breakpoint Triggers */ _dbg_setmode TRUE /* Debug Mode = True */ msr cpsr_c, #(MODE_ABT) /* Re-enable Interrupts */ _dbg_getstate r0 cmp r0, #DBG_CONFIGURED blo dbg__bkpt_waitCMD_cont /* Not configured yet, don't send Breakpoint Signal Response */ bl _dbg__cmd_Status /* Send signal response to the GDB server */ dbg__bkpt_waitCMD_cont: bl dbg__runloopTasks /* Execute housekeeping tasks while in ABRT mode */ bl dbg__getDebugMsg /* Read new message from Debugger, buflen in R0, 0 if none, -1 if error, msgbuf pointer in R1 */ cmp r0, #0 beq dbg__bkpt_waitCMD_cont /* No message yet, do housekeeping tasks */ bgt _proc_command /* valid message, process it */ bl __dbg__procChecksumError /* Message invalid, checksum error? */ b dbg__bkpt_waitCMD_cont _proc_command: /* Message now has Ctrl-C or $\0 */ mov r4, r1 /* Use R4 as buffer pointer */ ldrb r0, [r4], #1 /* Look for Ctrl-C or '$' */ teq r0, #MSGBUF_CTRLC bne _dbg_check_gdb_command /* Ctrl-C detected, do nothing (wait for next command from GDB) */ #if 0 /* On entering the Debugger via Ctrl-C, _dbg__cmd_Status has already sent a reply, so just keep quiet */ bl __dbg__procAckOnly /* send Ack */ #endif b dbg__bkpt_waitCMD_cont _dbg_check_gdb_command: teq r0, #MSGBUF_STARTCHAR movne r1, #MSG_ERRFORMAT /* Message Format invalid (not '$') */ bne _dbg__cmdError /* Shouldn't happen */ ldrb r0, [r4], #1 /* Look for command char */ bl _dbg__cmdChar2Index /* Index in R0 */ mov r1, #CMDINDEX_OUTOFRANGE teq r0, r1 bne _dbg__cmdExists /* Found valid command, execute it */ _dbg_unknown_command: bl _dbg__nop /* Command character not recognized, send empty response to GDB server */ b dbg__bkpt_waitCMD_cont #if 0 moveq r1, #MSG_UNKNOWNCMD /* Out of range, Command character not recognized */ beq _dbg__cmdError /* Send response to GDB server */ #endif _dbg__cmdExists: mov r3, r0 /* put Command Handler Index in R3 */ mov r0, r4 /* R0 now contains Input Message Buffer Parameter Pointer (previously in R4) */ _dbg_jumpTableHandler debug_cmdJumpTable, r2, r3 /* Call Command Handler Routine, use R2 as jump address pointer */ b dbg__bkpt_waitCMD_cont _dbg__cmdError: _dbg_outputMsgStatusErr bl dbg__putDebugMsg /* Send error response to the GDB server */ b dbg__bkpt_waitCMD_cont /* __dbg__procOkOnly * Send OK to GDB due to Detach * On entry: * None * On exit: * r0-r3: destroyed */ __dbg__procOkOnly: stmfd sp!, {lr} _dbg_outputMsgStatusOk /* Acknowledge Detach command from GDB server */ _cont_procOkOnly: bl dbg__putDebugMsg /* Send OK response to the GDB server */ cmp r0, #0 beq _cont_procOkOnly_exit /* Sending of OK succeeded */ bl dbg__runloopTasks /* Service run loop tasks */ b _cont_procOkOnly /* Retry OK transmission */ _cont_procOkOnly_exit: ldmfd sp!, {pc} /* __dbg__procAckOnly * Send Ack to GDB due to Continue or Step * On entry: * None * On exit: * r0-r3: destroyed */ __dbg__procAckOnly: stmfd sp!, {lr} _dbg_outputAckOnlyFlag b _cont_sendAckOrNak /* Acknowledge Continue or Step command from GDB server */ /* __dbg__procChecksumError * Request Retransmission from GDB due to Checksum error * On entry: * None * On exit: * r0-r3: destroyed */ __dbg__procChecksumError: stmfd sp!, {lr} _dbg_outputRetransmitFlag /* b _cont_sendAckOrNak */ /* Acknowledge Continue or Step command from GDB server */ _cont_sendAckOrNak: bl dbg__sendAckOrNak /* send Ack or Nak to GDB server */ cmp r0, #0 beq _cont_sendAckOrNak_exit /* Sending of Ack or Nak succeeded */ bl dbg__runloopTasks /* Service run loop tasks */ b _cont_sendAckOrNak /* Retry Ack or Nak transmission */ _cont_sendAckOrNak_exit: ldmfd sp!, {pc} /* _dbg__cmdChar2Index * Convert Command Character to Jump Table Index * On entry: * r0: command character * On exit: * r0: jump table index (-1 for command not found) * R1: destroyed * R2: destroyed * R3: destroyed */ _dbg__cmdChar2Index: mov r1, r0 /* Copy command character to r1 */ mov r0, #0 /* Clear return value */ ldr r3, =debug_cmdIndexTable /* Convert command to index using r3 as Index Lookup Address Pointer */ 1: ldrb r2, [r3, r0] /* Get table entry */ teq r2, #0 moveq r0, #CMDINDEX_OUTOFRANGE /* End of Index Table, Not found */ beq _exit_cmdIndexTable teq r1, r2 addne r0, #1 /* Increment Index */ bne 1b /* No match, skip to next command char */ _exit_cmdIndexTable: bx lr /* __dbg__cmdParamLen * Determines the length of the parameter buffer for a given command * On entry: * R0: parameter buffer pointer (contents after '$' and '') * On exit: * R0: Address of parameter buffer (preserved) * R1: length */ __dbg__cmdParamLen: stmfd sp!, {r0,r2,lr} /* R2: scratch register */ mov r1, #0 1: ldrb r2, [r0], #1 teq r2, #0 addne r1, r1, #1 bne 1b ldmfd sp!, {r0,r2,pc} /* __dbg__procCmdOk * Common subroutine exit stub to return Command Ok Status for Command Handlers * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack. * */ __dbg__procCmdOk: _dbg_outputMsgStatusOk b __dbg__sendDebugMsgExit /* __dbg__procUnimplementedError * Common subroutine exit stub to handle Unimplemented GDB Command Error * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack. * Note: GDB Remote Protocol specifies returning blank message ('+$') * */ __dbg__procUnimplementedError: _dbg_outputMsgValidResponse b __dbg__sendDebugMsgExit /* __dbg__procCmdParamError * Common subroutine exit stub to handle Command Parameter Error for Command Handlers * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack. * */ __dbg__procCmdParamError: mov r1, #MSG_UNKNOWNPARAM b __dbg__procErrorMsg /* __dbg__procCmdReturnInputLengthError * Common subroutine exit stub to handle Command Input Length Error for Command Handlers * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack. * */ __dbg__procCmdReturnInputLengthError: mov r1, #MSG_ERRINLENGTH b __dbg__procErrorMsg /* __dbg__procCmdReturnOutputLengthError * Common subroutine exit stub to handle Command Return Output Error for Command Handlers * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack. * */ __dbg__procCmdReturnOutputLengthError: mov r1, #MSG_ERROUTLENGTH b __dbg__procErrorMsg /* __dbg__procBreakpointAddrError * Common subroutine exit stub to handle Breakpoint Address Error for Breakpoint Insert/Remove Handlers * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack. * */ __dbg__procBreakpointAddrError: mov r1, #MSG_UNKNOWNBRKPT /* b __dbg__procErrorMsg */ __dbg__procErrorMsg: _dbg_outputMsgStatusErr /* b __dbg__sendDebugMsgExit */ __dbg__sendDebugMsgExit: bl dbg__putDebugMsg /* Send error response to the GDB server */ ldmfd sp!, {pc} /* _dbg__cmd_Status * Status Command Handler * On entry: * r0: parameter buffer (contents after '$' and '') * On exit: * r0, r1, r2, r3: destroyed */ _dbg__cmd_Status: stmfd sp!, {lr} _dbg_get_bkpt_type r0 _check_data_abort_exception: teq r0, #DBG_ABORT_DATA moveq r1, #MSG_SIG_BUS /* Bus Error */ beq _exit_dmg__cmd_Status _check_prefetch_abort_exception: teq r0, #DBG_ABORT_PREFETCH moveq r1, #MSG_SIG_ABRT /* FIMXE: Look for a better Signal number */ beq _exit_dmg__cmd_Status _default_breakpoint_exception: mov r1, #MSG_SIG_DEFAULT /* Dummy Signal number */ _exit_dmg__cmd_Status: _dbg_outputMsgStatusSig b __dbg__sendDebugMsgExit /* _dbg__cmd_Query * Query Command Handler * On entry: * r0: parameter buffer (contents after '$' and '') * [varied, see GDB General Packets query docs] * http://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html * On exit: * r0, r1, r2, r3: destroyed */ _dbg__cmd_Query: stmfd sp!, {r0,r1, lr} _dbg_setstate DBG_CONFIGURED /* We have exchanged query messages with the GDB server */ ldmfd sp!, {r0, r1} /* Restore parameters needed for subsequent processing */ bl __dbg__cmdParamLen cmp r1, #CMD_QUERY_MINPARAMLEN beq _dbg__cmd_Query_default ldrb r2, [r0] /* Get First Query Param Char */ _dbg__cmd_Query_check_C: teq r2, #CMD_QUERY_CURRTID_CHAR /* Handle Current Thread ID Query */ bne _dbg__cmd_Query_check_fThreadInfo cmp r1, #CMD_QUERY_CURRTID_PARAMLEN bne _dbg__cmd_Query_default _dbg_outputMsgCurrTID b __dbg__sendDebugMsgExit _dbg__cmd_Query_check_fThreadInfo: teq r2, #CMD_QUERY_FTINFO_CHAR /* Handle fThreadInfo Query */ bne _dbg__cmd_Query_check_sThreadInfo cmp r1, #CMD_QUERY_FTINFO_PARAMLEN bne _dbg__cmd_Query_default _dbg_outputMsgFirstThreadInfo b __dbg__sendDebugMsgExit _dbg__cmd_Query_check_sThreadInfo: teq r2, #CMD_QUERY_STINFO_CHAR /* Handle sThreadInfo ID Query */ bne _dbg__cmd_Query_default cmp r1, #CMD_QUERY_STINFO_PARAMLEN bne _dbg__cmd_Query_default _dbg_outputMsgSubsequentThreadInfo b __dbg__sendDebugMsgExit _dbg__cmd_Query_default: b __dbg__procUnimplementedError /* FIXME: return an empty message to GDB (no modifiable settings) */ /* _dbg__cmd_GetOneReg * Get One Register Value Command Handler * Valid command parameter x is from '0' to 'F' for User Mode Registers R0-R15, * '18' for FPSCR (dummy), and '19' for CPSR * On entry: * r0: parameter buffer pointer (contents after '$' and '') * x * On exit: * r0, r1, r2, r3, r4: destroyed * */ _dbg__cmd_GetOneReg: stmfd sp!, {lr} bl __dbg__cmdParamLen cmp r1, #CMD_REG_GETONE_MINPARAMLEN /* Check for correct length */ blo __dbg__procCmdParamError /* Unexpected input, report error */ cmp r1, #CMD_REG_GETONE_MAXPARAMLEN /* Check for correct length */ bhi __dbg__procCmdParamError /* Unexpected input, report error */ bl ascii2hex_varlen_be /* convert ASCII reg enum to Hex (in R0), R1 has address of next buffer char */ _dbg__proc_getRegister: mov r4, r0 /* Keep register index safe */ _dbg_outputMsgValidResponse /* R0: address of output message buffer data pointer (after response prefix) */ mov r1, r4 /* Move register index value to R1 */ bl _dbg_outputOneRegValue /* update output buffer */ _asciiz r0, r1 bl dbg__putDebugMsg /* Send response to the GDB server */ ldmfd sp!, {pc} /* _dbg_outputOneRegValue * Given Register Enum (0-F: R0-R15, 0x19: CPSR, OtherVal: dummy), output hex char to buffer * On entry: * r0: output message buffer pointer * r1: register enum (0-F, 0x19) * On exit: * r0: updated (points to next character slot at end of Output Buffer) * r1: original output message buffer pointer * r2: destroyed */ _dbg_outputOneRegValue: stmfd sp!, {lr} cmp r1, #REG_PC addls r2, r1, #DBGSTACK_USERREG_INDEX /* Convert register enum to Debug Stack index */ bls _retrieve_RegVal cmp r1, #REG_CPSR moveq r2, #DBGSTACK_USERCPSR_INDEX /* convert register enum to Debug Stack index */ beq _retrieve_RegVal bhi _exit_dbg_outputOneRegValue /* No match (reg enum > REG_CPSR), skip */ #if 0 cmp r1, #REG_FPSCR bne _exit_dbg_outputOneRegValue /* No match, skip */ #endif _output_dummy_regval: /* Output Dummy Register value (for F0-F7, FPSR) */ /* FIXME: F0-F7 expects output value that is more than 32-bits */ mov r1, #0 b _output2buffer /* Output all zeros for F0-F7, FPSCR */ _retrieve_RegVal: _getdbgregisterfromindex r2, r1 /* Retrieve Register contents into R1 */ _output2buffer: #ifdef __BIG_ENDIAN__ bl word2ascii_be /* Convert and put hex chars into Output Message Buffer */ #else bl word2ascii_le /* Convert and put hex chars into Output Message Buffer */ #endif _exit_dbg_outputOneRegValue: ldmfd sp!, {pc} /* _dbg__cmd_GetAllRegs * Get All Register Values Command Handler * Output Buffer returns register values in the order: User R0, R1, R2, ..., R15 * On entry: * r0: parameter buffer pointer (contents after '$' and '') * (no parameters) * On exit: * r0, r1, r2, r3: destroyed */ _dbg__cmd_GetAllRegs: stmfd sp!, {lr} bl __dbg__cmdParamLen teq r1, #CMD_REG_GETALL_PARAMLEN /* Check for correct length */ bne __dbg__procCmdParamError /* Unexpected input, report error */ _dbg_outputMsgValidResponse /* R0: address of output message buffer data pointer (after response prefix) */ /* We must return R0-R15, then CPSR */ mov r3, #REG_R0 /* Output User Register Values first */ 1: mov r1, r3 bl _dbg_outputOneRegValue /* update output buffer */ add r3, r3, #1 /* increment index */ cmp r3, #REG_PC bls 1b /* process all the registers */ #if 0 _get_cpsr: /* GDB will query for CPSR value specifically */ mov r1, #REG_CPSR /* Output User CPSR Value last */ bl _dbg_outputOneRegValue /* update output buffer */ #endif _asciiz r0, r1 bl dbg__putDebugMsg /* Send response to the GDB server */ ldmfd sp!, {pc} /* _dbg_setOneRegValue * Given Register Enum (0-F: R0-R15, 0x19: CPSR, OtherVal: dummy), output hex char to buffer * On entry: * r0: parameter buffer pointer * r1: register enum (0-F, 0x19) * On exit: * r0: (Updated) Address of parameter in buffer * r1, r2, r3: destroyed * * FIXME: This routine assumes that the input buffer contains exactly 8-Hex digits for each register value. */ _dbg_setOneRegValue: stmfd sp!, {lr} cmp r1, #REG_PC addls r2, r1, #DBGSTACK_USERREG_INDEX /* Convert register enum to Debug Stack index */ bls _store_RegVal cmp r1, #REG_CPSR moveq r2, #DBGSTACK_USERCPSR_INDEX /* convert register enum to Debug Stack index */ beq _store_RegVal bhi _exit_dbg_setOneRegValue /* No match (reg enum > REG_CPSR), skip */ #if 0 cmp r1, #REG_FPSCR bne _exit_dbg_setOneRegValue /* No match, skip */ #endif _set_dummy_regval: /* FIXME: This assumes that we will never get FP reg values (96-bits) in this routine */ /* Set dummy FPSCR value (ignored) */ add r1, r0, #CMD_REG_REGPARAMLEN /* Just increment the pointer */ b _done_store_RegVal _store_RegVal: #ifdef __BIG_ENDIAN__ bl ascii2word_be #else bl ascii2word_le #endif /* R0: value, R1: pointer to next char in buffer */ _setdbgregisterfromindex r2, r0, r3 /* Set Register contents in R0, using index in R2, and scratch register R3 */ _done_store_RegVal: mov r0, r1 /* Copy buffer pointer to next parameter to R0 for return value */ _exit_dbg_setOneRegValue: ldmfd sp!, {pc} /* _dbg__cmd_SetOneReg * Set One Register Value Command Handler * Valid command parameter x is from '0' to 'F' for User Mode Registers R0-R15, * '18' for FPSCR (dummy), and '19' for CPSR * On entry: * r0: parameter buffer pointer (contents after '$' and '') * x=rrrr * On exit: * r0, r1, r2, r3, r4: destroyed * */ _dbg__cmd_SetOneReg: stmfd sp!, {lr} bl __dbg__cmdParamLen cmp r1, #CMD_REG_SETONE_MINPARAMLEN /* Check for correct length */ blo __dbg__procCmdParamError /* Unexpected input, report error */ cmp r1, #CMD_REG_SETONE_MAXPARAMLEN /* Check for correct length */ bhi __dbg__procCmdParamError /* Unexpected input, report error */ /* FIXME: We can't set FP regs. * Fortunately the values required by FP regs are 96 bits (24 nibbles) * so it fails the CMD_REG_SETONE_MAXPARAMLEN check */ bl ascii2hex_varlen_be /* convert ASCII reg enum to Hex (in R0), R1 has address of next buffer char */ cmp r0, #REG_FPSCR beq __dbg__procCmdParamError /* Don't allow setting of FPSCR */ mov r4, r0 /* Keep enum in R4 */ _check_msgassignment r1 /* R1 points to next char in buffer */ bne __dbg__procCmdParamError /* Can't find '=' */ _dbg__proc_setRegister: mov r0, r1 /* move parameter buffer pointer to R0 */ mov r1, r4 /* and retrieve enum to R1 to call _dbg_setOneRegValue */ bl _dbg_setOneRegValue b __dbg__procCmdOk /* _dbg__cmd_SetAllReg * Set All Register Values Command Handler * On entry: * r0: parameter buffer pointer (contents after '$' and '') * rrrrRRRRrrrr... (17 registers) * On exit: * r0, r1, r2, r3, r4: destroyed * */ _dbg__cmd_SetAllRegs: /* Assumes that the registers are in the sequence R0, R1, ... R15 */ stmfd sp!, {lr} bl __dbg__cmdParamLen /* R0: pointer to parameters in buffer */ teq r1, #CMD_REG_SETALL_PARAMLEN /* Check for correct length */ bne __dbg__procCmdParamError /* Unexpected input, report error */ mov r4, #REG_R0 /* R4: register enum, starting with enum for R0 */ 1: mov r1, r4 /* Copy enum to R1; R0 already points to current parameter */ bl _dbg_setOneRegValue /* R0: next parameter address pointer */ add r4, r4, #1 /* increment index */ cmp r4, #REG_PC bls 1b ldrb r0, [r0] teq r0, #0 /* Look for ASCIIZ character to terminate loop */ beq __dbg__procCmdOk bne __dbg__procCmdParamError /* Unexpected input, report error */ /* _dbg__nop * NOP Command Handler (placeholder) * On entry: * r0: parameter buffer (contents after '$' and '') * On exit: * r0, r1, r2, r3: destroyed */ _dbg__nop: stmfd sp!, {lr} b __dbg__procUnimplementedError /* _dbg__cmd_ReadMem * Read Memory Contents Command Handler * Output Buffer returns memory contents * On entry: * r0: parameter buffer pointer (contents after '$' and '') * AA..AA,LLLL * On exit: * r0, r1, r2, r3, r4, r5: destroyed */ _dbg__cmd_ReadMem: stmfd sp!, {lr} bl __dbg__cmdParamLen #if 0 teq r1, #CMD_MEM_READ_PARAMLEN /* Check for correct length */ bne __dbg__procCmdParamError /* Unexpected input, report error */ bl ascii2word_be /* convert ASCII address location to Hex (in R0), R1 has address of next buffer char */ #endif bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */ mov r5, r0 /* Keep Address location in R5 */ _check_msgseparator r1 bne __dbg__procCmdParamError /* Can't find ',' */ mov r0, r1 /* move buffer pointer to R0 for subsequent processing */ bl ascii2hex_varlen_be /* convert ASCII length to Hex (in R0), R1 has address of next buffer char */ cmp r0, #CMD_MEM_MAXREADBYTES /* Don't overflow our buffer (2 x CMD_MEM_MAXREADBYTES) */ bhi __dbg__procCmdReturnOutputLengthError /* Requested Length greater than buffer size, return error */ mov r4, r0 /* Keep numbytes in R4 */ /* FIXME: Should validate the address? */ _dbg_outputMsgValidResponse /* R0: address of output message buffer data pointer (after response prefix) */ 1: ldrb r1, [r5], #1 bl byte2ascii /* update output buffer */ subs r4, r4, #1 bne 1b _asciiz r0, r1 bl dbg__putDebugMsg /* Send response to the GDB server */ ldmfd sp!, {pc} /* _dbg__cmd_WriteMem * Write Memory Contents Command Handler * On entry: * r0: parameter buffer pointer (contents after '$' and '') * AA..AA,LLLL::bb..bb * On exit: * r0, r1, r2, r3, r4: destroyed */ _dbg__cmd_WriteMem: stmfd sp!, {lr} bl __dbg__cmdParamLen #if 0 cmp r1, #CMD_MEM_WRITE_MINPARAMLEN /* Check for correct (minimum) length */ blo __dbg__procCmdParamError /* Unexpected input, report error */ sub r4, r1, #CMD_MEM_WRITE_MINPARAMLEN /* R4: Number of ASCII Hex chars for byte writes */ bl ascii2word_be /* convert ASCII address location to Hex (in R0), R1 has address of next buffer char */ #endif bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */ mov r3, r0 /* Keep Address location in R3 */ _check_msgseparator r1 bne __dbg__procCmdParamError /* Can't find ',' */ mov r0, r1 /* move buffer pointer to R0 for subsequent processing */ bl ascii2hex_varlen_be /* convert ASCII length to Hex (in R0), R1 has address of next buffer char */ cmp r0, r4, asr #1 /* is Number of bytes to write == (number of ASCII Hex Chars / 2)? */ bne __dbg__procCmdParamError /* Number of bytes does not match argument length */ cmp r0, #CMD_MEM_MAXWRITEBYTES /* Don't overflow our buffer (2 x CMD_MEM_MAXWRITEBYTES) */ bhi __dbg__procCmdReturnInputLengthError /* Requested Length greater than buffer size, return error */ mov r4, r0 /* Keep numbytes in R4 */ /* FIXME: Should validate the address? */ _check_msgargument r1 bne __dbg__procCmdParamError /* Can't find ':' */ 1: mov r0, r1 bl ascii2byte /* convert ASCII Hex value into byte value */ strb r0, [r3], #1 /* Store into memory location */ subs r4, r4, #1 bne 1b b __dbg__procCmdOk /* _dbg__cmd_Detach * Detach User Program Execution Command Handler * Treat this as being equivalent to 'Continue' without any arguments * * On entry: * r0: parameter buffer pointer (contents after '$' and '') * (No Parameters) * On exit: * r0-r7: destroyed * Note: This routine does not return to caller. Instead it switches * operating mode to UNDEF and returns to previously active program */ _dbg__cmd_Detach: stmfd sp!, {lr} /* In case unexpected parameters were received */ bl __dbg__cmdParamLen teq r1, #CMD_DETACH_PARAMLEN /* Check for correct length */ bne __dbg__procCmdParamError /* Unexpected input, report error */ ldmfd sp!, {lr} /* Cleanup stack, since we won't return to the Debugger Run Loop */ _dbg_setstate DBG_INIT /* Reset the Debug State */ bl __dbg__procOkOnly /* send OK to keep GDB server happy */ b _dbg__cont_check_breakpoint_type /* Continue from current PC */ /* _dbg__cmd_Continue * Continue User Program Execution Command Handler * Setup breakpoints before resuming execution of program. * * If Address is specified, update the next instruction address to specified address * * If this is a Normal Breakpoint, then we resume from current (Breakpoint) exception address * Else (it is a Manual Breakpoint or Address Specified) * We need to resume from the next instruction address * On entry: * r0: parameter buffer pointer (contents after '$' and '') * Optional: AA..AA * On exit: * r0-r7: destroyed * Note: This routine does not return to caller. Instead it switches * operating mode to UNDEF and returns to previously active program */ _dbg__cmd_Continue: /* Don't put anything on the stack, we won't return to the Debugger Run Loop */ bl __dbg__cmdParamLen cmp r1, #CMD_CONTINUE_MINPARAMLEN /* Check for correct parameter length */ bne _dbg__cont_fromAddr /* Address is specified */ bl __dbg__procAckOnly /* send Ack to keep GDB server happy */ b _dbg__cont_check_breakpoint_type /* Continue from current PC */ _dbg__cont_fromAddr: bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */ /* Continue from Specified Address */ _setdbgregister DBGSTACK_NEXTINSTR_INDEX, r0, r1 /* Set Next Instruction Pointer for Resume using contents of R0, and scratch register R1 */ bl __dbg__procAckOnly /* send Ack to keep GDB server happy */ b _dbg__cont_is_manual_bkpt_or_address_specified _dbg__cont_check_breakpoint_type: _dbg_get_bkpt_type r0 teq r0, #DBG_MANUAL_BKPT_ARM beq _dbg__cont_is_manual_bkpt_or_address_specified teq r0, #DBG_MANUAL_BKPT_THUMB beq _dbg__cont_is_manual_bkpt_or_address_specified _dbg__cont_is_normal_breakpoint: _getdbgregister DBGSTACK_USERPC_INDEX, r0 /* Retrieve Aborted Instruction PC from the Debug Stack into R0 */ _setdbgregister DBGSTACK_NEXTINSTR_INDEX, r0, r1 /* Set Next Instruction Pointer for Resume using contents of R0, and scratch register R1 */ /* GDB knows to Step from a breakpointed instruction before continuing when * Continue is issued, so it is safe to activate all defined breakpoints and continue. */ _dbg__cont_is_manual_bkpt_or_address_specified: bl _dbg__activate_breakpoints /* Restore exisiting breakpoints */ b _dbg__switch2undefmode /* _dbg__cmd_Step * Step User Program Execution Command Handler * Setup breakpoints before resuming execution of program. * * If Address is specified, update the next instruction address to specified address * * If this is a Normal Breakpoint, then we need to install a Step Breakpoint at next instruction address * and resume from current (Breakpoint) exception address * Else (it is a Manual Breakpoint or Address specified) * We need to install a Step Breakpoint at the following instruction address (after the next instruction address) * and resume from the next instruction address * On entry: * r0: parameter buffer pointer (contents after '$' and '') * Optional: AA..AA * On exit: * r0-r7: destroyed * Note: This routine does not return to caller. Instead it switches * operating mode to UNDEF and returns to previously active program */ _dbg__cmd_Step: /* Don't put anything on the stack, we won't return to the Debugger Run Loop */ bl __dbg__cmdParamLen cmp r1, #CMD_STEP_MINPARAMLEN /* Check for correct parameter length */ beq _dbg__step_check_breakpoint_type /* Step from current PC */ _dbg__step_fromAddr: bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */ /* Step from Specified Address */ _setdbgregister DBGSTACK_NEXTINSTR_INDEX, r0, r1 /* Set Next Instruction Pointer for Resume using contents of R0, and scratch register R1 */ b _dbg__step_is_manual_bkpt_or_address_specified _dbg__step_check_breakpoint_type: _dbg_get_bkpt_type r0 teq r0, #DBG_MANUAL_BKPT_ARM beq _dbg__step_is_manual_bkpt teq r0, #DBG_MANUAL_BKPT_THUMB beq _dbg__step_is_manual_bkpt _dbg__step_is_normal_breakpoint: _getdbgregister DBGSTACK_USERPC_INDEX, r0 /* Retrieve Aborted Instruction PC from the Debug Stack into R0 */ _setdbgregister DBGSTACK_NEXTINSTR_INDEX, r0, r1 /* Set Next Instruction Pointer for Resume usinh contents in R0, and scratch register R1 */ b _dbg__step_is_manual_bkpt_or_address_specified _dbg__step_is_manual_bkpt: _getdbgregister DBGSTACK_NEXTINSTR_INDEX, r0 /* Retrieve Next Instruction Pointer for Resume into R1 */ /* b _dbg__step_is_manual_bkpt_or_address_specified */ _dbg__step_is_manual_bkpt_or_address_specified: bl dbg_following_instruction_addr /* following instruction address returned in r1 */ bl dbg__install_singlestep /* Setup Single Step, next instruction address returned in r1 */ bl dbg__activate_singlestep #if 0 /* Disable ACK transmit, let the next Breakpoint generate the Signal Response */ bl __dbg__procAckOnly /* send Ack to keep GDB server happy */ #endif b _dbg__switch2undefmode /* _dbg__cmd_Kill * Kill User Program Execution Command Handler * Kill Program, this is accomplished by rebooting the Brick * * On entry: * r0: parameter buffer pointer (contents after '$' and '') * (No Parameters) * On exit: * r0-r7: destroyed * Note: This routine does not return to caller. Instead it calls * the relevant system routine to reboot the Brick */ _dbg__cmd_Kill: stmfd sp!, {lr} /* In case unexpected parameters were received */ bl __dbg__cmdParamLen teq r1, #CMD_KILL_PARAMLEN /* Check for correct length */ bne __dbg__procCmdParamError /* Unexpected input, report error */ bl __dbg__procAckOnly /* send Ack to keep GDB server happy */ b dbg__reboot /* Goodbye.... */ /* _dbg__proc_brkpt_params * Process Breakpoint Parameters * On entry: * r0: parameter buffer pointer (contents after '$' and '') * t,AA..AA,k * On exit: * r0: non-zero = breakpoint address; 0 = parameter error * r1: destroyed * r2: destroyed * r3: destroyed */ _dbg__proc_brkpt_params: /* FIXME: Add support for watchpoints */ stmfd sp!, {lr} mov r3, r0 /* Keep parameter buffer address in R3 */ ldrb r0, [r3], #1 /* get breakpoint type t */ bl char2hex cmp r0, #CMD_BKPT_TYPE_BREAK_MEMORY bne _dbg__proc_brkpt_params_error /* We only support memory breakpoints for now */ _check_msgseparator r3 bne _dbg__proc_brkpt_params_error /* Something wrong with the parameters */ mov r0, r3 /* Check Address */ bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */ mov r3, r0 /* Keep breakpoint address in R3 */ _check_msgseparator r1 bne _dbg__proc_brkpt_params_error /* Something wrong with the parameters */ ldrb r0, [r1], #1 /* get breakpoint kind k */ bl char2hex cmp r0, #CMD_BKPT_KIND_THUMB orreq r3, r3, #1 /* Mark Thumb breakpoints */ beq _exit_dbg__proc_brkpt_params cmp r0, #CMD_BKPT_KIND_ARM beq _exit_dbg__proc_brkpt_params /* ARM breakpoint */ _dbg__proc_brkpt_params_error: mov r3, #0 /* Unrecognized breakpoint type */ _exit_dbg__proc_brkpt_params: mov r0, r3 /* return breakpoint address */ ldmfd sp!, {pc} /* _dbg__cmd_InsertBreakpoint * Add Breakpoint * On entry: * r0: parameter buffer pointer (contents after '$' and '') * t,AA..AA,k * On exit: * r0, r1, r2, r3: destroyed */ _dbg__cmd_InsertBreakpoint: stmfd sp!, {lr} bl __dbg__cmdParamLen teq r1, #CMD_BKPT_INSERT_MINPARAMLEN /* Check for correct length */ blo __dbg__procCmdParamError /* Unexpected input, report error */ bl _dbg__proc_brkpt_params /* R0: Breakpoint Address */ teq r0, #0 beq __dbg__procBreakpointAddrError /* Thumb2 instructions, or unknown kind */ mov r3, r0 /* Keep breakpoint address in R3 */ mov r0, #0 /* Empty Breakpoint entry */ bl _dbg_find_breakpoint_slot /* Look for an available breakpoint slot, return index in R0 */ cmp r0, #CMD_BKPT_NOTFOUND beq __dbg__procBreakpointAddrError /* No empty slot! */ mov r1, r3 /* Move breakpoint address to R1 */ bl _dbg__install_one_breakpoint /* r0: index, r1: instruction address */ b __dbg__procCmdOk /* _dbg__cmd_RemoveBreakpoint * Remove Breakpoint * On entry: * r0: parameter buffer pointer (contents after '$' and '') * t,AA..AA,k * On exit: * r0, r1, r2, r3: destroyed */ _dbg__cmd_RemoveBreakpoint: stmfd sp!, {lr} bl __dbg__cmdParamLen teq r1, #CMD_BKPT_REMOVE_MINPARAMLEN /* Check for correct length */ blo __dbg__procCmdParamError /* Unexpected input, report error */ bl _dbg__proc_brkpt_params /* R0: Breakpoint Address */ teq r0, #0 beq __dbg__procBreakpointAddrError /* Thumb2 instructions, or unknown kind */ bl _dbg_find_breakpoint_slot /* Look for matching breakpoint slot, return index in R0 */ cmp r0, #CMD_BKPT_NOTFOUND beq __dbg__procBreakpointAddrError /* Specified Breakpoint not found! */ _index2bkptindex_addr r0, r1 /* Calculate Breakpoint Entry Address */ mov r0, r1 /* Move it to R0 for subroutine call */ bl _dbg__clear_one_breakpoint /* R0: address of breakpoint to clear */ b __dbg__procCmdOk /**************************************************************************** * * Breakpoint Manipulation Routines * ****************************************************************************/ /* _dbg_find_breakpoint_slot * Find the matching Breakpoint Slot. * This is both used to find empty slots (pass R0=0x0000) or * occupied slots (pass R0=) * * On Entry: * R0: Breakpoint Address * On Exit: * R0: Matching Index (-1: not found) * * NOTE: This routine performs exact match, i.e., breakpoint address MUST be configured * for ARM or Thumb (bit 0 clear/set) as appropriate */ _dbg_find_breakpoint_slot: stmfd sp!, {r1,r2,r3, lr} mov r1, #1 /* Only consider Breakpoints 1-7 */ ldr r3, =__breakpoints_num__ 1: _index2bkptindex_addr r1, r2 /* Calculate Breakpoint Entry Address */ ldr r2, [r2] /* Get actual breakpoint entry (instruction address) */ cmp r0, r2 beq _found_breakpoint_slot add r1, r1, #1 /* no match, check next */ cmp r1, r3 blo 1b /* continue checking only if we don't exceed __breakpoints_num__ */ _notfound_breakpoint_slot: mov r1, #CMD_BKPT_NOTFOUND _found_breakpoint_slot: mov r0, r1 /* Return value in R0 */ ldmfd sp!, {r1,r2,r3, pc} /* _dbg__clear_singlestep * Clear the Single Step Breakpoint */ _dbg__clear_singlestep: ldr r0, =__breakpoints_start__ /* Single Step Breakpoint is at the beginning of the Breakpoint State Struct */ /* b _dbg__clear_one_breakpoint */ /* _dbg__clear_one_breakpoint * On entry, R0 contains the Breakpoint State slot address to be cleared * */ _dbg__clear_one_breakpoint: mov r1, #0 mov r2, #0 stmea r0!, {r1, r2} /* clear Breakpoint state */ bx lr /* _dbg__clear_breakpoints * Routine iterates through the array of breakpoints (incl single step breakpoint) and clears the breakpoint */ _dbg__clear_breakpoints: stmfd sp!, {lr} ldr r0, =__breakpoints_start__ /* Single Step Breakpoint is at the beginning of the Breakpoint State Struct */ ldr r3, =__breakpoints_end__ /* start from top of the table */ 3: bl _dbg__clear_one_breakpoint cmp r0, r3 blo 3b ldmfd sp!, {pc} /* dbg__install_singlestep * Install the Single Step Breakpoint * * On entry: * R1: Instruction Address (31 bits, b0 = THUMB flag) * On exit: * R0: Breakpoint index (preserved) * R1: Instruction Address (preserved) * R2: Breakpoint Instruction * R3: Breakpoint Entry address */ dbg_interwork dbg__install_singlestep mov r0, #0 /* b _dbg__install_one_breakpoint */ /* _dbg__install_one_breakpoint * Install breakpoint entry into Breakpoint State Table * * On entry: * R0: Breakpoint index (assumed valid) * R1: Instruction Address (31 bits, b0 = THUMB flag) * On exit: * R0: Breakpoint index (preserved) * R1: Instruction Address (preserved) * R2: Breakpoint Instruction * R3: Breakpoint Entry address */ _dbg__install_one_breakpoint: /* Check for Thumb bit */ tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */ /* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */ bic r2, r1, #BKPT_STATE_THUMB_FLAG /* R2: Instruction Address; clear Thumb Flag for accessing Memory */ ldreq r2, [r2] /* if 0: load ARM instruction from address location */ ldrneh r2, [r2] /* else load Thumb instruction */ _index2bkptindex_addr r0, r3 /* Calculate Breakpoint Entry Address */ stm r3, {r1, r2} bx lr /* _dbg__restore_singlestep * Restores the contents of the single step breakpoint to memory * * On entry: * None * On exit: * R0-R2: Destroyed */ _dbg__restore_singlestep: mov r0, #0 /* single step breakpoint index */ _index2bkptindex_addr r0, r1 /* Calculate Single Step Breakpoint Entry Address */ ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ teq r1, #0 bxeq lr /* Exit if not active */ /* b _dbg__restore_one_breakpoint */ /* _dbg__restore_one_breakpoint * Restores the contents to memory for one breakpoint * * On entry: * R0: Breakpoint index (assumed valid) [not used -- can be used for validating BKPT] * R1: Breakpoint Address (assumed valid) * R2: Breakpoint Instruction (assumed valid) * On exit: * R0: Breakpoint index (preserved) * R1: B0 cleared if Thumb Instruction Address * R2: Breakpoint Instruction (preserved) */ _dbg__restore_one_breakpoint: /* Check for Thumb bit */ tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */ /* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */ streq r2, [r1] /* if 0: restore ARM instruction to address location */ bicne r1, #BKPT_STATE_THUMB_FLAG /* else, clear Thumb Flag */ strneh r2, [r1] /* store Thumb instruction */ bx lr /* _dbg__restore_breakpoints * Routine iterates through the array of breakpoints (incl single step breakpoint) and restores the contents to memory * Only Active breakpoints (i.e., Non-zero Address) are processed. * * On entry: * None * On exit: * R0-R6: Destroyed */ _dbg__restore_breakpoints: stmfd sp!, {lr} ldr r6, =_dbg__restore_one_breakpoint b __dbg__iterate_breakpoint_array /* dbg__activate_singlestep * Activate the single step breakpoint to memory * * On entry: * None * On exit: * R0-R3: Destroyed */ dbg_interwork dbg__activate_singlestep mov r0, #0 /* single step breakpoint index */ _index2bkptindex_addr r0, r1 /* Calculate Single Step Breakpoint Entry Address */ ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ teq r1, #0 bxeq lr /* Exit if not active */ /* b _dbg__activate_one_breakpoint */ /* _dbg__activate_one_breakpoint * Activate one breakpoint to memory * * On entry: * R0: Breakpoint index (assumed valid) * R1: Breakpoint Address (assumed valid) * R2: Breakpoint Instruction (assumed valid) * On exit: * R0: Breakpoint index (preserved) * R1-R3: Destroyed */ _dbg__activate_one_breakpoint: /* Check for Thumb bit */ tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */ bne _nx_is_thumb_bp _nx_is_arm_bp: /* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */ ldr r3, [r1] /* if 0: load ARM instruction from address location */ teq r2, r3 /* check that the two instructions are identical */ bne _dbg__breakpoint_invalid_arm ldr r2, =BKPT32_INSTR /* ARM BKPT instruction */ orr r2, r2, r0 /* Merge Breakpoint index */ str r2, [r1] /* Store it into memory location */ _dbg__breakpoint_invalid_arm: bx lr _nx_is_thumb_bp: bic r1, #BKPT_STATE_THUMB_FLAG /* else, clear Thumb Flag */ ldrh r3, [r1] /* load Thumb instruction from address location */ teq r2, r3 /* check that the two instructions are identical */ bne _dbg__breakpoint_invalid_thumb ldr r2, =BKPT16_INSTR /* Thumb BKPT instruction */ orr r2, r2, r0 /* Merge Breakpoint index */ strh r2, [r1] /* Store it into memory location */ _dbg__breakpoint_invalid_thumb: bx lr /* _dbg__activate_breakpoints * Routine iterates through the array of breakpoints (incl single step breakpoint) and activates them * Only Active breakpoints (i.e., Non-zero Address) are processed. * * On entry: * None * On exit: * R0-R6: Destroyed */ _dbg__activate_breakpoints: stmfd sp!, {lr} ldr r6, =_dbg__activate_one_breakpoint b __dbg__iterate_breakpoint_array /* __dbg__iterate_breakpoint_array * WARNING: DO NOT CALL THIS ROUTINE DIRECTLY. * Internal Routine, assumes that lr has been push to stack * * Common routine to iterate through the array of breakpoints (incl single step breakpoint) * Only Active breakpoints (i.e., Non-zero Address entries) are processed. * * On Entry: * R0: Breakpoint index * R1: Breakpoint Address * R2: Breakpoint Instruction * R6: Handler Routine * On exit: * R0-R5: Destroyed * R6: Handler routine (preserved) * */ __dbg__iterate_breakpoint_array: ldr r5, =__breakpoints_end__ /* start from top of the table (Assume __breakpoints_end__ > __breakpoints_start__) */ ldr r4, =__breakpoints_start__ /* end address check */ ldr r0, =__breakpoints_num__ /* Number of Breakpoints (incl Single Step) (Assume Non-Zero) */ 4: sub r0, r0, #1 /* Decrement breakpoint index in r0 */ ldmea r5!, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */ teq r1, #0 /* Is it active? */ movne lr, pc bxne r6 /* active entry */ cmp r5, r4 bhi 4b /* if (pointer > start of Breakpoint Table address), get next slot */ ldmfd sp!, {pc} nxt-firmware-1.29.7/armdebug/Debugger/debug_stub.h000066400000000000000000000111211466344546000220530ustar00rootroot00000000000000/** @file debug_stub.h * @brief Shared C/ASM header file for debugger stub * */ /* Copyright (C) 2007-2010 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #ifndef __DEBUG_STUB_H__ #define __DEBUG_STUB_H__ #include "_c_arm_macros.h" /** @name BKPT suppport constants * * ARM and Thumb Breakpoint Instructions. */ /*@{*/ #define __ARM6OR7__ #ifdef __ARM6OR7__ #define BKPT32_INSTR 0xE7200070 /* ARM6 and ARM7 does not trap unused opcodes (BKPT overlap with control instructions), \ CPU has unpredictable behavior. Ref: Steve Furber, ARM SoC Architecture 2nd Ed, pg. 143 */ #else #define BKPT32_INSTR 0xE1200070 /* ARM BKPT instruction, will work in ARMv5T and above */ #endif #define BKPT32_ENUM_MASK 0x000FFF0F /* ARM BKPT Enum Mask */ #define BKPT32_AUTO_BKPT 0x00080000 /* RESERVED: ARM BKPT Auto-Step Flag (for CONT support) */ #define BKPT32_MANUAL_BKPT 0x0007FF0F /* Manually inserted ARM Breakpoint */ #define BKPT16_INSTR 0xBE00 /* Thumb BKPT instruction */ #define BKPT16_ENUM_MASK 0x00FF /* Thumb BKPT Enum Mask */ #define BKPT16_AUTO_BKPT 0x0080 /* RESERVED: Thumb BKPT Auto-Step Flag (for CONT support) */ #define BKPT16_MANUAL_BKPT 0x007F /* Manually inserted Thumb Breakpoint */ /*@}*/ #ifndef __ASSEMBLY__ /* Define C stuff */ /** @defgroup debug_public */ /*@{*/ /** Initialize Debugger. * Equivalent to GDB set_debug_traps() routine */ FUNCDEF void dbg__bkpt_init(void); #ifdef __NXOS__ /** Communication Channel Enums * * Communication Channel Enums. */ ENUM_BEGIN ENUM_VALASSIGN(COMM_USB, 1) /**< USB Communications */ ENUM_VAL(COMM_BT) /**< Bluetooth Communications */ ENUM_END(comm_chan_t) /** Enable switch to Debugger Mode from NxOS operational mode * Returns 0 if no mode switch needed (already in Debug mode) * !0 if mode switch will happen * Used by NxOS only */ /* Note: This platform specific routine is found in debug_runlooptasks.S */ FUNCDEF int nxos__handleDebug(U8 *buffer, comm_chan_t channel, U32 length); #else /** Switch Mode to Debugger. * Used by NXT Firmware only */ /* Note: This platform specific routine is found in debug_runlooptasks.S */ FUNCDEF UWORD cCommHandleDebug(UBYTE *pInBuf, UBYTE CmdBit, UWORD MsgLength); #endif /** Debugger Handler Routine (called by Exception Handler Trap). * Equivalent to GDB handle_exception() routine */ FUNCDEF void dbg__bkpt_handler(void); /** dbg_breakpoint_arm. * Equivalent to GDB breakpoint() routine for ARM code */ /* FUNCDEF void dbg_breakpoint_arm(void); */ static inline void dbg_breakpoint_arm(void) { asm volatile (".word %a0" : /* Output (empty) */ : "X" (BKPT32_INSTR | BKPT32_MANUAL_BKPT) ); } #if 0 /* Old asm definitions, in case gas does not recognize %a0 operand */ #ifdef __ARM6OR7__ static inline void dbg_breakpoint_arm(void) { asm volatile (".word 0xE727FF7F" /* (BKPT32_INSTR | BKPT32_MANUAL_BKPT) */ ); } #else static inline void dbg_breakpoint_arm(void) { asm volatile (".word 0xE127FF7F" /* (BKPT32_INSTR | BKPT32_MANUAL_BKPT) */ ); } #endif #endif /** dbg_breakpoint_thumb. * Equivalent to GDB breakpoint() routine for Thumb code */ /* FUNCDEF void dbg_breakpoint_thumb(void); */ static inline void dbg_breakpoint_thumb(void) { asm volatile (".hword %a0" : /* Output (empty) */ : "X" (BKPT16_INSTR | BKPT16_MANUAL_BKPT) ); } #if 0 /* Old asm definitions, in case gas does not recognize %a0 operand */ static inline void dbg_breakpoint_thumb(void) { asm volatile (".hword 0xBE7F" /* (BKPT16_INSTR | BKPT16_MANUAL_BKPT) */); } #endif /*@}*/ #else /* Define Assembly stuff */ /* dbg__bkpt_arm * GDB breakpoint() for ARM mode */ .macro dbg__bkpt_arm .word (BKPT32_INSTR | BKPT32_MANUAL_BKPT) .endm /* dbg__bkpt_thumb * GDB breakpoint() for Thumb mode */ .macro dbg__bkpt_thumb .hword (BKPT16_INSTR | BKPT16_MANUAL_BKPT) .endm /** Macro to declare Interworking ARM Routine * * dbg_interwork * * Note: declared as a private macro since ARMDEBUG is also used by NIF */ .macro dbg_interwork arm_routine .align 4 .arm .type \arm_routine, %function @ Needed by new binutils (>2.21) .global \arm_routine \arm_routine: .endm #endif /*@}*/ #endif /* __DEBUG_STUB_H__ */ nxt-firmware-1.29.7/armdebug/Debugger/debug_test.S000066400000000000000000000062631466344546000220430ustar00rootroot00000000000000/** @file debug_test.S * @brief Test Routines to trigger ARM and Thumb Manual Breakpoints * */ /* Copyright (C) 2007-2011 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #define __ASSEMBLY__ #include "debug_stub.h" .text .align 4 .code 32 /********************************************** * dbg__test_arm_bkpt Test Routine * */ dbg_interwork dbg__test_arm_bkpt stmfd sp!,{lr} dbg__bkpt_arm /* Trigger ARM Manual Breakpoint */ ldmfd sp!,{pc} /********************************************** * dbg__test_arm_instrstep Test Routine * Used to test GDB Stepping command * This routine exercises the mov, add, ldr, ldm, b, bl, * and bx instructions which modify the PC (R15) * In addition, conditional instructions are also evaluated. * */ dbg_interwork dbg__test_arm_instrstep stmfd sp!, {lr} bl dbg__test_arm_instr_sub1 ldr r1, =test_arm_3 /* R1: pointer to test_arm_3 */ ldr r2, =test_arm_2 /* R2: pointer to test_arm_2 */ mov pc, r1 test_arm_1: subs r0, r0, #1 addne pc, r2, #4 /* If R0 > 0, keep branching to a new location */ /* else R0 == 0 */ b exit_dbg__test_arm_instrstep test_arm_2: sub r0, r0, #1 cmp r0, #5 bgt test_arm_1 ldrle pc, =exit_dbg__test_arm_instrstep b exit_dbg__test_arm_instrstep test_arm_3: sub r0, r0, #1 teq r0, #8 beq test_arm_1 ldrne r3, =test_arm_3 bx r3 exit_dbg__test_arm_instrstep: bl dbg__test_thumb_instr_sub1 ldmfd sp!, {pc} .global dbg__test_arm_instr_sub1 dbg__test_arm_instr_sub1: mov r0, #10 bx lr .global dbg__test_arm_instr_sub1 dbg__test_arm_instr_sub2: stmfd sp!, {r4, lr} mov r0, #TRUE ldmfd sp!, {r4, pc} /********************************************** * dbg__test_thumb_bkpt Test Routine * */ dbg_interwork dbg__test_thumb_bkpt stmfd sp!,{lr} /* ldr r0, =_thumb_entry orr r0, r0, #1 @ Set Thumb mode mov lr, pc bx r0 */ bl _thumb_entry ldmfd sp!,{pc} .code 16 .thumb_func .type _thumb_entry, %function _thumb_entry: dbg__bkpt_thumb bx lr /********************************************** * dbg__test_thumb_instrstep Test Routine * Used to test GDB Stepping command * */ .global dbg__test_thumb_instrstep .thumb_func .type dbg__test_thumb_instrstep, %function dbg__test_thumb_instrstep: push {lr} bl dbg__test_thumb_instr_sub1 bl dbg__test_thumb_instr_sub2 test_thumb_1: sub r0, #1 bne test_thumb_2 /* else R0 == 0 */ b exit_dbg__test_thumb_instrstep test_thumb_2: sub r0, #1 cmp r0, #5 bls test_thumb_1 bhi test_thumb_3 beq test_thumb_2 b test_thumb_1 test_thumb_3: sub r0, #1 cmp r0, #0xB blo load_test_thumb_1 ldr r2, =test_thumb_3+1 /* Need to set Thumb bit */ b exit_test_thumb_3 load_test_thumb_1: ldr r2, =test_thumb_1+1 /* Need to set Thumb bit */ exit_test_thumb_3: bx r2 exit_dbg__test_thumb_instrstep: bl dbg__test_arm_instr_sub1 pop {r1} bx r1 .thumb_func .type dbg__test_thumb_instr_sub1, %function dbg__test_thumb_instr_sub1: mov r0, #0x0F bx lr .thumb_func .type dbg__test_thumb_instr_sub2, %function dbg__test_thumb_instr_sub2: push {lr} mov r1, #FALSE pop {pc} .end nxt-firmware-1.29.7/armdebug/Debugger/debug_test.h000066400000000000000000000016321466344546000220630ustar00rootroot00000000000000/** @file debug_test.h * @brief C header file for debugger test routines * */ /* Copyright (C) 2007-2010 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * See COPYING for redistribution license * */ #ifndef __DEBUG_TEST_H__ #define __DEBUG_TEST_H__ #include "_c_arm_macros.h" #ifndef __ASSEMBLY__ /* Define C stuff */ /** @defgroup debug_public */ /*@{*/ /** * Insert ARM Breakpoint instruction into code stream */ FUNCDEF void dbg__test_arm_bkpt(void); /** * Insert Thumb Breakpoint instruction into code stream */ FUNCDEF void dbg__test_thumb_bkpt(void); /** * Dummy function for testing ARM instruction stepping */ FUNCDEF void dbg__test_arm_instrstep(void); /** * Dummy function for testing Thumb instruction stepping */ FUNCDEF void dbg__test_thumb_instrstep(void); /*@}*/ #endif #endif /* __DEBUG_TEST_H__ */ nxt-firmware-1.29.7/armdebug/Debugger/undef_handler.S000066400000000000000000000142301466344546000225050ustar00rootroot00000000000000 /* Copyright (C) 2007-2010 the NxOS developers * * Module Developed by: TC Wan * * See AUTHORS for a full list of the developers. * * Redistribution of this file is permitted under * the terms of the GNU Public License (GPL) version 2. */ #define __ASSEMBLY__ #include "debug_stub.h" #include "debug_internals.h" .text .code 32 .align 0 .extern dbg__thumb_bkpt_handler .extern dbg__arm_bkpt_handler .extern default_undef_handler /* Remote GDB Debugger relies on BKPT instruction being trapped here * In ARMv4t, it is an Illegal (Undefined) Instruction. * On triggering, lr (R14) contains the previous mode's pc (R15). * Based on example in Hohl, "ARM Assembly Language: Fundamentals and Techniques" * Chapter 11, Example 11.1. */ /** undef_handler * We assume that the DEBUG stack holds only one stack frame and we will overwrite it. * On entry, LR_undef points to one instruction past the UNDEF instruction. * * For the purpose of Debugging, the stack frame should present the PC (R15) as the address * of the instruction that triggered the Breakpoint. Hence we need to adjust R15 * to point to the address of the UNDEF instruction. This is what the JTAG debugger * does. * * We will also store UNDEF LR (next instruction pointer) and UNDEF SPSR to the stack. * * For the handler, once the user registers have been stored in the DEBUG stack, the * registers will be used as follows: * * R0: UNDEF LR, then UNDEF instruction address, finally UNDEF instruction word / BKPT index * R1: SPSR * R2: Mode * R3: Debug Stack Pointer (for Banked R13-R14 update) */ dbg_interwork undef_handler ldr sp, =__debugger_stack__ stmfd sp, {r0-r15}^ /* Save workspace, previous mode's pc via 'S' flag, R13-R15: placeholders */ mov r3, sp /* Use R3 to write Banked R13-R14, and actual PC of UNDEF instruction */ sub sp, sp, #(4*16) /* Need to manually update SP(undef) */ mov r0, lr /* Keep Next Instruction address after UNDEF instruction in R0 */ mrs r1, spsr /* Copy SPSR to r1 */ stmfd sp!, {r0,r1} /* Save User's Next Instr Pointer (in UNDEF LR) and previous mode's CPSR to stack */ tst r1, #CPSR_THUMB /* Check for Thumb Mode */ subne r0, r0, #2 /* Is Thumb instruction, adjust PC for UNDEF instruction address */ subeq r0, r0, #4 /* Is ARM instruction, adjust PC for UNDEF instruction address */ str r0, [r3, #-4]! /* Save PC to stack (R15 slot) */ and r2, r1, #CPSR_MODE /* Get previous mode */ teq r2, #MODE_USR beq _skip_banked_registers /* Can't switch back if we're in User mode! */ _store_prev_mode_banked_regs: /* FIXME: We don't handle FIQ properly! */ orr r2, #(CPSR_FIQ | CPSR_IRQ) /* Disable Interrupts */ msr cpsr_c, r2 /* Switch to previous mode */ stmfd r3!, {sp, lr} /* Store Previous Mode's LR (R14), SP (R13) via R3 */ msr cpsr_c, #(MODE_UND | CPSR_FIQ | CPSR_IRQ) /* Revert to Undef Mode */ _skip_banked_registers: tst r1, #CPSR_THUMB /* Check for Thumb Mode */ beq _is_arm /* Clear, so it's ARM mode */ _is_thumb: ldrh r0, [r0] /* load UNDEF instruction into r0 */ ldr r1, =BKPT16_ENUM_MASK /* Thumb BKPT enum mask */ bic r2, r0, r1 /* leave only opcode */ ldr r1, =BKPT16_INSTR /* check for Thumb Breakpoint Instruction */ teq r2, r1 bne default_undef_handler ldr r1, =BKPT16_ENUM_MASK /* get Thumb BKPT Enum Mask */ ldr r2, =dbg__thumb_bkpt_handler /* handle BKPT, BKPT index in r0 */ b _exit_undef_handler _is_arm: ldr r0, [r0] /* load UNDEF instruction into r0 */ ldr r1, =BKPT32_ENUM_MASK /* ARM BKPT enum mask */ bic r2, r0, r1 /* leave only opcode */ ldr r1, =BKPT32_INSTR /* check for ARM Breakpoint Instruction */ teq r2, r1 bne default_undef_handler ldr r1, =BKPT32_ENUM_MASK /* get ARM BKPT Enum Mask */ ldr r2, =dbg__arm_bkpt_handler /* handle BKPT, BKPT index in r0 */ _exit_undef_handler: and r0, r1, r0 /* Keep index value */ msr cpsr_c, #(MODE_ABT | CPSR_FIQ | CPSR_IRQ) /* Switch to Abort Mode, Disable Interrupts */ ldr sp, =__abort_stack__ /* Reinitialize stack pointer each time a Breakpoint happens */ bic sp, sp, #7 mov pc, r2 /* Invoke Debugger */ /** resume_execution * This routine is called by the Debugger prior to returning control to * the executing program. * It updates the SPSR_UNDEF with the Debug Stack value, and * restores all registers R0-R14 to the previously active mode. * Then, it uses the Next Instruction Address Pointer to return * execution control to the previously executing program. */ /* On Entry, SP(undef) points to the Next Instruction Address. * If the instruction which triggered the Breakpoint need to be * reexecuted, it should be placed in the Next Instruction Address slot * by ABORT mode before coming here */ dbg_interwork resume_execution ldr lr, =__debugger_stack_bottom__ /* Use LR(undef) for Debug Stack Access */ add r1, lr, #(DBGSTACK_USERSP_INDEX*4) /* Use R1 for Previous Mode SP (R13) and LR (R14) access */ ldr r0, [lr, #(DBGSTACK_USERCPSR_INDEX*4)]! /* LR updated, Retrieve SPSR into R0 */ msr spsr, r0 /* Update SPSR for return to program being debugged */ and r0, r0, #CPSR_MODE /* Get previous mode */ teq r0, #MODE_USR bne _restore_prev_mode_banked_regs /* Can't switch back if we're in User mode! */ /* Previous mode was User Mode */ ldmed lr, {r0-r14}^ /* We use LDMED since LR is pointing to USERCPSR not R0 */ b _really_resume_execution _restore_prev_mode_banked_regs: /* FIXME: We don't handle FIQ properly! */ orr r0, #(CPSR_FIQ | CPSR_IRQ) /* Disable Interrupts */ msr cpsr_c, r0 /* Switch to previous mode */ ldmfd r1, {sp, lr} /* Restore Previous Mode's LR (R14), SP (R13) via R1 */ msr cpsr_c, #(MODE_UND | CPSR_FIQ | CPSR_IRQ) /* Revert to Undef Mode */ ldmed lr, {r0-r12} /* We use LDMED since LR is pointing to USERCPSR not R0 */ _really_resume_execution: ldmfd sp, {pc}^ /* Exit to Previous Mode using Next Instruction Address */ nxt-firmware-1.29.7/armdebug/Doxyfile000066400000000000000000000215241466344546000175510ustar00rootroot00000000000000# Doxyfile 1.5.3-20071008 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "NxOS Debugger" PROJECT_NUMBER = OUTPUT_DIRECTORY = ../doc/debug CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO #DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = YES CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = . INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.h RECURSIVE = YES EXCLUDE = at91sam7s256.h EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 1 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 280 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES #MACRO_EXPANSION = NO MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO nxt-firmware-1.29.7/armdebug/GNU-GPLv2.txt000066400000000000000000000431311466344546000201230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. nxt-firmware-1.29.7/armdebug/Host/000077500000000000000000000000001466344546000167545ustar00rootroot00000000000000nxt-firmware-1.29.7/armdebug/Host/README000066400000000000000000000014021466344546000176310ustar00rootroot00000000000000The nxt-gdb-server.py script is initially developed by Nicolas Schodet. It depends on the following libraries: - nxt-python v2.2.x http://code.google.com/p/nxt-python/ Additional Dependencies For Windows and MacOSX: - Fantom Drivers from LEGO Additional Dependencies For Linux: - pyusb v0.4x http://pyusb.wiki.sourceforge.net - libusb v0.1.x http://libusb.org/ Currently, it does not work with libusb v1.x; on Mac OSX, it causes segfaults. Installation ============ There is no specific installation requirements (at this moment). Just run the python script in a terminal window. Usage ===== 1. Connect the USB cable from the PC Host to the NXT 2. start nxt-gdb-server.py 3. start arm-none-eabi-gdb, configured as remote for port 2828 (default) nxt-firmware-1.29.7/armdebug/Host/gdb-commands.txt000066400000000000000000000013271466344546000220530ustar00rootroot00000000000000# This file contains hand coded GDB commands for testing the GDB Server <-> NXT interface # Display all Registers $g#67 # Display R0 $p0#A0 # Display R1 $p1#A1 # Display PC $pF#B6 # Display FPSCR (dummy) $p18#D9 # Display User CPSR $p19#DA # Query Status $?#3F # Query Thread $qC#B4 # Set R1 to 0xAA $P1=000000AA#60 # Read 16 bytes of Memory from 0x00201d74 (padding bytes after debug_mode + 4 bytes of debug_InUSBBuf) $m00201D74,0010#FC # Write 2 bytes of memory to 0x00201d74 (padding bytes after debug_mode) $M00201D74,0002:AA55#03 # Write 2 bytes of memory to 0x00201d74 (padding bytes after debug_mode) $M00201D74,0002:9966#F5 # GDB Read Instruction at Address (PC) +$m1001de,4#58 # Continue Execution $c#63 nxt-firmware-1.29.7/armdebug/Host/nxt-gdb-server.py000077500000000000000000000225771466344546000222150ustar00rootroot00000000000000#!/usr/bin/env python # # Copyright (C) 2011 the NxOS developers # # Module Developed by: Nicolas Schodet # TC Wan # # See AUTHORS for a full list of the developers. # # See COPYING for redistribution license # # Exchange GDB messages with the NXT brick. # # Every message is encapsulated with the debug command and message length. # This can be used by the firmware to make the distinction between debug # messages and regular messages. # import nxt.locator import socket import optparse import select try: import pyfantom except ImportError: import usb import struct import sys CTRLC = chr(3) NAKCHAR = '-' ACKCHAR = '+' STATUS_QUERY = "$?#3F" DEFAULT_PORT = 2828 SELECT_TIMEOUT = 0.1 DEBUG = False DEBUG2 = False NXT_RECV_ERR = -1 # Libusb 0.12.x blocks on USB reads LIBUSB_RECEIVE_BLOCKING = True class NXTGDBServer: # Socket read size. recv_size = 1024 # Maximum message size. pack_size = 61 # Debug command header, no reply. debug_command = 0x8d def __init__ (self, port, nowait): """Initialise server.""" self.nowait = nowait self.port = port self.in_buf = '' self.brick = None def pack (self, data, segment_no): """Return packed data to send to NXT.""" # Insert command and length. assert len (data) <= self.pack_size return struct.pack ('BBB', self.debug_command, segment_no, len (data)) + data def unpack (self, data): """Return unpacked data from NXT.""" # May be improved, for now, check command and announced length. if len (data) == 0: return '', 0 # No message, exit if len (data) < 3: return '', NXT_RECV_ERR header, body = data[0:3], data[3:] command, segment_no, length = struct.unpack ('BBB', header) if command != self.debug_command or length != len (body): return '', NXT_RECV_ERR return body, segment_no def segment (self, data): """Split messages in GDB commands and make segments with each command.""" segs = [ ] self.in_buf += data # Find ACK '+' end = self.in_buf.find (ACKCHAR) while end == 0: self.in_buf = self.in_buf[end+1:] # Strip out any leading ACKCHAR if DEBUG2: print "stripped ACK, remain: ", self.in_buf end = self.in_buf.find (ACKCHAR) # Find NAK '-' end = self.in_buf.find (NAKCHAR) if end == 0: msg, self.in_buf = self.in_buf[0:end+1], self.in_buf[end+1:] segs.append (self.pack (msg, 0)) end = self.in_buf.find (NAKCHAR) # Find Ctrl-C (assumed to be by itself and not following a normal command) end = self.in_buf.find (CTRLC) if end >= 0: msg, self.in_buf = self.in_buf[0:end+1], self.in_buf[end+1:] assert len (msg) <= self.pack_size, "Ctrl-C Command Packet too long!" segs.append (self.pack (msg, 0)) end = self.in_buf.find (CTRLC) end = self.in_buf.find ('#') # Is # found and enough place for the checkum? while end >= 0 and end < len (self.in_buf) - 2: msg, self.in_buf = self.in_buf[0:end + 3], self.in_buf[end + 3:] i = 0 gdbprefix = msg[i] while gdbprefix in [ACKCHAR]: # Ignore any '+' i += 1 gdbprefix = msg[i] if DEBUG2: print "Checking '", gdbprefix, "'" assert gdbprefix == '$', "not a GDB command" # Make segments. seg_no = 0 while msg: seg, msg = msg[0:self.pack_size], msg[self.pack_size:] seg_no += 1 if not msg: # Last segment. seg_no = 0 segs.append (self.pack (seg, seg_no)) # Look for next one. end = self.in_buf.find ('#') return segs def reassemble (self, sock): msg = '' prev_segno = 0 segno = NXT_RECV_ERR # force initial pass through while loop while segno != 0: try: s, segno = self.unpack (sock.recv ()) if len (s) == 0: if segno == 0 and prev_segno == 0: return '' # No message pending else: segno = NXT_RECV_ERR # Keep waiting for segments # Ignore error packets if segno >= 0: # Check segno, if non-zero it must be monotonically increasing from 1, otherwise 0 if segno > 0: assert segno == prev_segno + 1, "segno = %s, prev_segno = %s" % (segno, prev_segno) prev_segno = segno msg += s except IOError as e: # Some pyusb are buggy, ignore some "errors". if e.args != ('No error', ): raise e return msg def run (self): """Endless run loop.""" # Create the listening socket. s = socket.socket (socket.AF_INET, socket.SOCK_STREAM) s.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind (('', self.port)) s.listen (1) while True: # We should open the NXT connection first, otherwise Python startup delay # may cause GDB to misbehave if not self.nowait: dummy = raw_input('Waiting...Press when NXT GDB Stub is ready. ') # Open connection to the NXT brick. self.brick = nxt.locator.find_one_brick () self.brick.sock.debug = DEBUG # Wait for a connection. print "Waiting for GDB connection on port %s..." % self.port client, addr = s.accept () print "Client from", addr # Work loop, wait for a message from client socket or NXT brick. while client is not None: data = '' # Wait for a message from client or timeout. rlist, wlist, xlist = select.select ([ client ], [ ], [ ], SELECT_TIMEOUT) for c in rlist: assert c is client # Data from client, read it and forward it to NXT brick. data = client.recv (self.recv_size) data = data.strip() if len (data) > 0: #if len (data) == 1 and data.find(CTRLC) >= 0: # print "CTRL-C Received!" # data = STATUS_QUERY if DEBUG: if data[0] == CTRLC: print "[GDB->NXT] " else: print "[GDB->NXT] %s" % data segments = self.segment (data) data = '' for seg in segments: try: self.brick.sock.send (seg) except IOError as e: # Some pyusb are buggy, ignore some "errors". if e.args != ('No error', ): raise e if segments != [] and LIBUSB_RECEIVE_BLOCKING: if DEBUG2: print "Accessing Blocking sock.recv()" data = self.reassemble (self.brick.sock) else: client.close () client = None if not LIBUSB_RECEIVE_BLOCKING: if DEBUG2: print "Accessing Non-Blocking sock.recv()" data = self.reassemble (self.brick.sock) # Is there something from NXT brick? if data: if DEBUG: print "[NXT->GDB] %s" % data if client: client.send (data) data = '' self.brick.sock.close() print "Connection closed." if self.nowait: break if __name__ == '__main__': # Read options from command line. parser = optparse.OptionParser (description = """ Gateway between the GNU debugger and a NXT brick. """) parser.add_option ('-p', '--port', type = 'int', default = DEFAULT_PORT, help = "server listening port (default: %default)", metavar = "PORT") parser.add_option ('-v', '--verbose', action='store_true', dest='verbose', default = False, help = "verbose mode (default: %default)") parser.add_option ('-n', '--nowait', action='store_true', dest='nowait', default = False, help = "Don't wait for NXT GDB Stub Setup before connecting (default: %default)") (options, args) = parser.parse_args () if args: parser.error ("Too many arguments") # Run. try: DEBUG = options.verbose if DEBUG: print "Debug Mode Enabled!" server = NXTGDBServer (options.port, options.nowait) server.run () except KeyboardInterrupt: print "\n\nException caught. Bye!" if server.brick is not None: server.brick.sock.close() sys.exit() nxt-firmware-1.29.7/armdebug/LEGO_Open_Source_License.doc000066400000000000000000001200001466344546000232100ustar00rootroot00000000000000ࡱ> HJI@ Lbjbjצצ"\D  $ b        $RQB  B  W       ø  m 0    B B   LEGO OPEN SOURCE LICENSE AGREEMENT 1.0 LEGO MINDSTORMS NXT FIRMWARE This LEGO Open Source License Agreement is an open source license for the firmware of the LEGO MINDSTORMS NXT microprocessor. IMPORTANT -- READ CAREFULLY: THIS AGREEMENT ONLY COVERS THE FIRMWARE OF THE LEGO MINDSTORMS NXT MICROPROCESSOR AND DOES NOT COVER ANY "SOFTWARE" AS DEFINED IN THE MINDSTORMS NXT END USER LICENSE AGREEMENT (HEREINAFTER THE " MINDSTORMS NXT EULA"). Section 1. Definitions. "Contributor'' means each entity that creates or contributes to the creation of Modifications. ''Contributor Version'' means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. ''Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. ''Executable'' means Covered Code in any form other than Source Code. "Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. ''License'' means this document. ''Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is (a) any addition to or deletion from the contents of a file containing Original Code or previous Modifications, or (b) any new file that contains any part of the Original Code or previous Modifications. "Original Code" means Source Code for the firmware of the LEGO MINDSTORMS NXT microprocessor, and which, at the time of its release under this License is not already Covered Code governed by this License. ''Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. "You'' (or "Your")means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. Section 2. Source Code License and Larger Works. 2.1. LEGO Grant. LEGO grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims, to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work. 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive licenseto use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work. 2.3. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. Section 3. Distribution Obligations. 3.1. Application of License. The Modifications which You create or to which You contribute shall be governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via a generally accepted mechanism for the electronic transfer of data (hereinafter "Electronic Distribution Mechanism"), to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by LEGO and including the name of LEGO in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. 3.4. Intellectual Property Matters. (a) Third Party Claims. If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL'' which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. (b) Contributor APIs. If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. (c) Representations. Contributor represents that, except as disclosed pursuant to Section 3.4(a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Section 3.1-3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You must distribute the Executable version of Covered Code under the terms of this License. Section 4. Inability to Comply Due to Statute or Regulation. If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. Section 5. Exclusions. Notwithstanding anything to the contrary herein, no Software (as defined in the Mindstorms NXT EULA) shall be released or made available as open source under this License, regardless of how any Covered Code interacts with any Software (as defined in the Mindstorms NXT EULA). Section 6. Versions of the License. 6.1. New Versions. LEGO may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. 6.2. Effect of New Versions. Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by LEGO. No one other than LEGO has the right to modify the terms applicable to Covered Code created under this License. Section 7. Disclaimer of Warranty. ALL COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT LEGO OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. Section 8. Termination. 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against LEGO or a Contributor (LEGO or Contributor against whom You file such action is referred to as "Participant")alleging that such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i)agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. Section 9. Limitation of Liability. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, LEGO, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. Section 10. U.S. Government End Users. The Covered Code is a ''commercial item,'' as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ''commercial computer software'' and ''commercial computer software documentation,'' as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. Section 11. Responsibility for Claims. As between LEGO and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with LEGO and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. Section 12. Trademarks. This License does not grant, nor shall any provision of this License be construed as granting, any rights or permission to use the trade names, trademarks, service marks, or product names of LEGO. Section 13. Governing Law To the extent possible under applicable law, this License shall be governed by Danish law and shall be subject to the non-exclusive jurisdiction of the Commercial and Maritime Court of Copenhagen. This License will not be governed by the United Nations Convention of Contracts for the International Sale of Goods, the application of which is hereby expressly excluded. You acknowledge that the export of any Covered Code is governed by the export control laws of the United States of America and other countries. If you are downloading any Covered Code, You represent and warrant that You are not located in or under the control of any country which the export laws and regulations of such country or of the United States prohibit the exportation of the Covered Code. Section 14. Miscellaneous. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. EXHIBIT A - LEGO Open Source License Agreement The contents of this file are subject to the LEGO Open Source License Agreement Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.______________.com Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. LEGO is the owner of the Original Code. Portions created by _________________ are Copyright (C) _________. All Rights Reserved. Contributor(s): ______________________________________. G F I A X T _ v &Bu"j|rlo 9p""##%%@%׽h0g0JCJOJQJ^J h0g0J5CJOJQJ\^Jh0g5PJ\h0gPJaJh0g5PJ\aJ h0g>* h0gPJh0g h0g5\E(GH A T v jrp ^```$a$Lp"#%q'*Y*Z*,,.,.-...i0j0005363O3P3^` ^` ^``@%I%&&q''''((*W*Z*++,,,-8----...*.-.?...j00063M3P3V344u9{9P;T;O<s<v<@+@.@A BB|CC_D{DGGGI I!IDIrIILλȻĴ h0g5\h0g5PJ\h0g h0gPJh0g0JCJOJQJ^J h0g0J5CJOJQJ\^Jh0gPJaJh0g5PJ\aJBP34u9P;O<u<v<@@-@.@AA BB|C}CCC^D_D{D|DGGGGI^``IIIEI:JK[KKL^$a$^ 1h/ =!"#$%@@@ NormalCJ_HaJmH sH tH NA@N Standardskrifttype i afsnitZi@Z Tabel - Normal :V 44 la 6k@6Ingen oversigt B^@B Normal (Web)dd[$\$Tg@T HTML-skrivemaskineCJOJPJQJ^JaJD\(GHATv j rpq"Y"Z"$$&,&-&&&i(j(((5+6+O+P+,u1P3O4u4v488-8.899 ::|;};;;^<_<{<|<????AAAEA:BC[CCD?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0000000000000000000000000000000000000000000000000000000000000000000000@%L'+pP3IL(*,-L)DDRJ0g@DT D`@Unknowngz Times New RomanTimes New RomanUSymbolTimes New RomanG& z ArialHelveticaW5  z Courier NewCourier New"hcfcf&) 9"{) 9"{qr4dCC3QH?RJ#LEGO OPEN SOURCE LICENSE AGREEMENTCOBriendkpetstrOh+'0 (4 P \ h t$LEGO OPEN SOURCE LICENSE AGREEMENTEGOCOBrienOBrOBr Normal.dotS dkpetstrtS2peMicrosoft Word 10.0@@@x@x) 9Root Entry FnwHM@Data /1Table7WordDocument"\  !"#$%&'()*+,-.012345689:;<=>?ABCDEFGLNK  Source on the firmwear. SummaryInformation(@DocumentSummaryInformation8CompObjj  FMicrosoft Word-dokument MSWordDocWord.Document.89q՜.+,D՜.+,` hp  Cantor Colburn LLP{"C: $LEGO OPEN SOURCE LICENSE AGREEMENT Titel`H<DLTX_AdHocReviewCycleID_EmailSubject _AuthorEmail_AuthorEmailDisplayName_PreviousAdHocReviewCycleID|n"peter.strandgaard@europe.lego.comPeter StrandgaardA Open Source EULA and License for Opennxt-firmware-1.29.7/armdebug/README000066400000000000000000000010411466344546000167130ustar00rootroot00000000000000Introduction ============ armdebug is an ARM Assembly Language Instruction debugger for the NXT. It is intended for embedding in the NXT firmware (e.g. NXT Improved Firmware), as well as for use with NxOS. Contents ======== The various folders contents are as follows: Debugger: GDB client driver for NXT (need to be embedded in firmware code) Host: GDB Server for PC Host LICENSES ======== The armdebug code is dual-licensed. Please see COPYING for more details. Other projects included in this repository have their respective licenses. nxt-firmware-1.29.7/armdebug/SConscript000066400000000000000000000004001466344546000200430ustar00rootroot00000000000000# This scons build script is used by NxOS from glob import glob Import('env') for source in glob('Debugger/*.[cS]'): obj = env.Object(source.split('.')[0], source) env.Append(NXOS_DEBUG=obj) if env['WITH_DOXYGEN']: env.Doxygen('Doxyfile') nxt-firmware-1.29.7/armdebug/SConstruct000066400000000000000000000153021466344546000200720ustar00rootroot00000000000000# -*- mode: python -*- ############################################################### # This scons build script is used to check the armdebug project # code for syntax errors. It does not build working executable # code since it links to external routines. ############################################################### import os import os.path import new from glob import glob ############################################################### # Utility functions. ############################################################### # Similar to env.WhereIs, but always searches os.environ. def find_on_path(filename): paths = os.environ.get('PATH') if not paths: return None for p in paths.split(':'): path = os.path.abspath(os.path.join(p, filename)) if os.path.isfile(path): return p return None # Run the given gcc binary, and parses its output to work out the gcc # version. def determine_gcc_version(gcc_binary): stdout = os.popen('%s --version' % gcc_binary) gcc_output = stdout.read().split() stdout.close() grab_next = False for token in gcc_output: if grab_next: return token elif token[-1] == ')': grab_next = True return None # Check that a given cross-compiler tool exists. If it does, the path is # added to the build environment, and the given environment variable is # set to the tool name. # # This is used to check for the presence of a working cross-compiler # toolchain, and to properly set up the environment to do it. See below # in the configuration section for details. def CheckTool(context, envname, toolname=None, hostprefix=None): toolname = toolname or envname.lower() if hostprefix is None: hostprefix = '%s-' % context.env['CROSS_COMPILE_HOST'] toolname = '%s%s' % (hostprefix, toolname) context.Message("Checking for %s..." % toolname) toolpath = find_on_path(toolname) if not toolpath: context.Result('not found') return False else: context.Result('ok') context.env[envname] = toolname context.env.AppendENVPath('PATH', toolpath) return True # Find the correct variant and version of libgcc.a in the cross-compiler # toolchain. def CheckLibGcc(context, gccname): context.Message("Locating a cross-compiled libgcc...") toolpath = find_on_path(gccname) if not toolpath: context.Result("%s not found" % toolname) return False gcc_version = determine_gcc_version(gccname) if not gcc_version: context.Result("Could not determine gcc version") return False gcc_install_dir = os.path.split(os.path.normpath(toolpath))[0] for libdir in ['interwork', 'thumb', '']: libgcc_path = os.path.join(gcc_install_dir, 'lib', 'gcc', context.env['CROSS_COMPILE_HOST'], gcc_version, libdir, 'libgcc.a') if os.path.isfile(libgcc_path): break if not os.path.isfile(libgcc_path): context.Result("libgcc.a not found") return False context.Result("ok - " + libgcc_path) context.env.Append(LIBGCC=libgcc_path) return True def CheckDoxygen(context): context.Message("Looking for Doxygen...") doxypath = find_on_path('doxygen') if doxypath: context.Result("ok") context.env.AppendENVPath('PATH', doxypath) context.env['WITH_DOXYGEN'] = True else: context.Result("not found") context.env['WITH_DOXYGEN'] = False ############################################################### # Options that can be provided on the commandline ############################################################### opts = Variables('scons.options', ARGUMENTS) opts.Add(PathVariable('gccprefix', 'Prefix of the cross-gcc to use (by default arm-none-eabi)', 'arm-none-eabi', PathVariable.PathAccept)) Help(''' Type: 'scons' to build object files. - To use another cross-gcc than arm-none-eabi-gcc: scons gccprefix=arm-softfloat-eabi Options are saved persistent in the file 'scons.options'. That means after you have called e.g. 'scons gccprefix=arm-softfloat-eabi' it's enough to call only 'scons' to build both using the new gcc version again. ''') ############################################################### # Construct and configure a cross-compiler environment ############################################################### env = Environment(options = opts, tools = ['gcc', 'as', 'gnulink', 'ar'], toolpath = ['scons_tools'], LIBGCC = [], CPPPATH = '#', WITH_DOXYGEN = False) opts.Update(env) opts.Save('scons.options', env) if not env.GetOption('clean'): conf = Configure(env, custom_tests = {'CheckTool': CheckTool, 'CheckLibGcc': CheckLibGcc, 'CheckDoxygen': CheckDoxygen}) conf.env['CROSS_COMPILE_HOST'] = env['gccprefix'] if not (conf.CheckTool('CC', 'gcc') and conf.CheckTool('AR') and conf.CheckTool('OBJCOPY') and conf.CheckTool('LINK', 'ld') and conf.CheckLibGcc(conf.env['CC'])): print "Missing or incomplete arm-elf toolchain, cannot continue!" Exit(1) env = conf.Finish() mycflags = ['-mcpu=arm7tdmi', '-Os', '-Wextra', '-Wall', '-Werror', '-Wno-div-by-zero', '-Wfloat-equal', '-Wshadow', '-Wpointer-arith', '-Wbad-function-cast', '-Wmissing-prototypes', '-ffreestanding', '-fsigned-char', '-ffunction-sections', '-std=gnu99', '-fdata-sections', '-fomit-frame-pointer', '-msoft-float'] myasflags = ['-Wall', '-Werror', '-Os']; if str(env['LIBGCC']).find('interwork') != -1: mycflags.append('-mthumb-interwork') myasflags.append('-Wa,-mcpu=arm7tdmi,-mfpu=softfpa,-mthumb-interwork') elif str(env['LIBGCC']).find('thumb') != -1: mycflags.append('-mthumb') myasflags.append('-Wa,-mcpu=arm7tdmi,-mfpu=softfpa,-mthumb') else: myasflags.append('-Wa,-mcpu=arm7tdmi,-mfpu=softfpa') mycflags.append('-g') mycflags.append('-ggdb') # Big Endian Output (disabled by default) #mycflags.append('-D__BIG_ENDIAN__') # Test build for NxOS (Comment out for NXT Firmware) mycflags.append('-D__NXOS__') myasflags.append('-g') myasflags.append('-ggdb') # Big Endian Output (disabled by default) #mycflags.append('-D__BIG_ENDIAN__') # Test build for NxOS (Comment out for NXT Firmware) myasflags.append('-D__NXOS__') env.Replace(CCFLAGS = mycflags, ASFLAGS = myasflags ) # Build the baseplate, and all selected application kernels. numProcs = os.sysconf('SC_NPROCESSORS_ONLN') SConscript(['SConscript'], 'numProcs env CheckTool') nxt-firmware-1.29.7/contrib/000077500000000000000000000000001466344546000157115ustar00rootroot00000000000000nxt-firmware-1.29.7/contrib/README000066400000000000000000000003411466344546000165670ustar00rootroot00000000000000The nxt-update-firmware script can be used in distribution packages to ease the flashing of a firmware binary installed on the system. You may have to customize it to match the installation path and the fwflash package name. nxt-firmware-1.29.7/contrib/_incr_version000077500000000000000000000004671466344546000205050ustar00rootroot00000000000000#!/bin/sh -eux # Written by Nicolas Schodet, who stated the code does not qualify for # copyright protection. # # Only update patch version. [ ${1%.*} = ${2%.*} ] a=${1##*.} b=${2##*.} sed -i src/c_loader.iom -e "s/\\(FIRMWAREPATCH *\\)$a/\\1$b/" git add src/c_loader.iom git commit -m "Change to version ${2}" nxt-firmware-1.29.7/contrib/nxt-update-firmware000077500000000000000000000007551466344546000215510ustar00rootroot00000000000000#!/bin/sh # Written by Dominik George , who stated the code does not # qualify for copyright protection. pathfind() { OLDIFS="$IFS" IFS=: for p in $PATH; do if [ -x "$p/$*" ]; then IFS="$OLDIFS" return 0 fi done IFS="$OLDIFS" return 1 } if ! pathfind fwflash; then echo You need to install the libnxt package to flash firmware to NXT bricks. >&2 exit 1 fi fwflash /usr/share/nxt-firmware/nxt_firmware.bin nxt-firmware-1.29.7/contrib/nxt-update-firmware.1000066400000000000000000000014731466344546000217030ustar00rootroot00000000000000.Dd October 19, 2016 .Dt NXT-UPDATE-FIRMWARE 1 .Os Debian .Sh NAME .Nm nxt\-update\-firmware .Nd wrapper script to flash NXT firmware .Sh SYNOPSIS .Nm .Sh DESCRIPTION .Nm is a wrapper script that uses the .Nm fwflash utility from the libnxt package to flash the improved LEGO NXT firmware to an NXT brick. .Pp The script calls .Nm fwflash with the default path to the firmware in Debian and returns its error code. .Sh FILES .Bl -tag -width Ds -compact .It Pa /usr/share/nxt\-firmware/nxt_firmware.bin Path to the NXT firmware file. .El .Sh SEE ALSO .Xr fwflash 1 .Sh AUTHORS Written for the Debian project by .An Dominik George Aq Mt nik@naturalnet.de . The code does not qualify for copyright protection; it is shipped with the nxt\-firmware package, see the copyright file under .Pa /usr/share/doc/nxt\-firmware/copyright . nxt-firmware-1.29.7/data/000077500000000000000000000000001466344546000151625ustar00rootroot00000000000000nxt-firmware-1.29.7/data/bitmaps/000077500000000000000000000000001466344546000166215ustar00rootroot00000000000000nxt-firmware-1.29.7/data/bitmaps/Cursor.png000066400000000000000000000001211466344546000205760ustar00rootroot00000000000000PNG  IHDRثIDATxc``L '<$IENDB`nxt-firmware-1.29.7/data/bitmaps/Cursor.toml000066400000000000000000000000521466344546000207700ustar00rootroot00000000000000format = "bitmap" start_x = 0 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/Display.png000066400000000000000000000001641466344546000207350ustar00rootroot00000000000000PNG  IHDRHq;IDATxc4;A?|dRq05YчȈFP3#ܗ<<[IENDB`nxt-firmware-1.29.7/data/bitmaps/Display.toml000066400000000000000000000000541466344546000211220ustar00rootroot00000000000000format = "bitmap" start_x = 14 start_y = 16 nxt-firmware-1.29.7/data/bitmaps/Fail.png000066400000000000000000000001701466344546000202000ustar00rootroot00000000000000PNG  IHDR U?IDATxm!@PEW֮;$ 3>E2-'rv*fAgҎ1rKT1W >UWIENDB`nxt-firmware-1.29.7/data/bitmaps/Fail.toml000066400000000000000000000000521466344546000203660ustar00rootroot00000000000000format = "bitmap" start_x = 0 start_y = 8 nxt-firmware-1.29.7/data/bitmaps/Info.png000066400000000000000000000001661466344546000202250ustar00rootroot00000000000000PNG  IHDR U=IDATxu̱ 0P$Db.:X4lLA ׸Vkg܍ xX?=﫨IENDB`nxt-firmware-1.29.7/data/bitmaps/Info.toml000066400000000000000000000000521466344546000204060ustar00rootroot00000000000000format = "bitmap" start_x = 0 start_y = 8 nxt-firmware-1.29.7/data/bitmaps/LowBattery.png000066400000000000000000000004751466344546000214310ustar00rootroot00000000000000PNG  IHDR`8*>IDATxAjPEOT.@p JW];q$JJ6&vM~RxGp/ψ+2}X{Ar fa4h+IePeGk#?+w.!?&=qbohpa"#tI*=rxb"I*-X:-5{|,gmo m[xMx'/,7CHp\]H݇)S&:gmL;q+38_kN[QIENDB`nxt-firmware-1.29.7/data/bitmaps/LowBattery.toml000066400000000000000000000000521466344546000216070ustar00rootroot00000000000000format = "bitmap" start_x = 2 start_y = 8 nxt-firmware-1.29.7/data/bitmaps/Ok.png000066400000000000000000000001601466344546000176750ustar00rootroot00000000000000PNG  IHDR77IDATx @џh c7+*Q\T\)=d/G.a& r*y ?ھK Tɬ';쎑tn|W O/&7`.Z3;I^7cRRIENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_1.toml000066400000000000000000000000531466344546000214440ustar00rootroot00000000000000format = "bitmap" start_x = 16 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/RCXintro_10.png000066400000000000000000000001401466344546000213320ustar00rootroot00000000000000PNG  IHDR 'IDATx=± 07sv~b/kg 9BZB;MeCIENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_10.toml000066400000000000000000000000541466344546000215250ustar00rootroot00000000000000format = "bitmap" start_x = 56 start_y = 32 nxt-firmware-1.29.7/data/bitmaps/RCXintro_11.png000066400000000000000000000001331466344546000213350ustar00rootroot00000000000000PNG  IHDR"IDATx%A TL bE62Kp'y $F'E\DRT9wP\*cfh pAl{.xv~s޸gM=m3e&H?O*2%";lS̯ף*=Ƽ@#T=b^-EA `RVuwOaffVJi P" * |듖\Ca2KrE\ f#vIENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_2.toml000066400000000000000000000000531466344546000214450ustar00rootroot00000000000000format = "bitmap" start_x = 16 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/RCXintro_3.png000066400000000000000000000003631466344546000212630ustar00rootroot00000000000000PNG  IHDR@@LsIDATx1J@owCA#R<{ Ff ^J^]$Z䁖>aFRC/kh(B=a3"8}۝/Ƕ؊ U:3=3N9>a,gq>h#mޱi=|96Hu$]!ΰ2 V/'BuIENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_3.toml000066400000000000000000000000531466344546000214460ustar00rootroot00000000000000format = "bitmap" start_x = 16 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/RCXintro_4.png000066400000000000000000000002331466344546000212600ustar00rootroot00000000000000PNG  IHDR@@LsbIDATxб B1 ` %Q` P$BtH胛w,Y2IaPg`WN /\ oN$ir-au| IENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_4.toml000066400000000000000000000000531466344546000214470ustar00rootroot00000000000000format = "bitmap" start_x = 16 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/RCXintro_5.png000066400000000000000000000002431466344546000212620ustar00rootroot00000000000000PNG  IHDR4@}ijIDATx P 壟#8u["R_>$$$x===OK9: Ǹ?Mu_mzNᚙ7O7>]Vq8|~3IENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_5.toml000066400000000000000000000000531466344546000214500ustar00rootroot00000000000000format = "bitmap" start_x = 23 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/RCXintro_6.png000066400000000000000000000002131466344546000212600ustar00rootroot00000000000000PNG  IHDR,0BLRIDATx10CC13A3+ pj%w9ShtjMBg W%L_'lVnJ2IENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_6.toml000066400000000000000000000000531466344546000214510ustar00rootroot00000000000000format = "bitmap" start_x = 28 start_y = 8 nxt-firmware-1.29.7/data/bitmaps/RCXintro_7.png000066400000000000000000000002051466344546000212620ustar00rootroot00000000000000PNG  IHDR"( LIDATx̱ 0 BJp)FiЉK tx@\cCf%<׷Pڨ4̝ps[I k;IENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_7.toml000066400000000000000000000000541466344546000214530ustar00rootroot00000000000000format = "bitmap" start_x = 35 start_y = 16 nxt-firmware-1.29.7/data/bitmaps/RCXintro_8.png000066400000000000000000000001611466344546000212640ustar00rootroot00000000000000PNG  IHDR08IDATxch``a@8 ~ L tg zIENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_8.toml000066400000000000000000000000541466344546000214540ustar00rootroot00000000000000format = "bitmap" start_x = 44 start_y = 24 nxt-firmware-1.29.7/data/bitmaps/RCXintro_9.png000066400000000000000000000001411466344546000212630ustar00rootroot00000000000000PNG  IHDR(IDATxc@BJӋ.#F'T1RIENDB`nxt-firmware-1.29.7/data/bitmaps/RCXintro_9.toml000066400000000000000000000000541466344546000214550ustar00rootroot00000000000000format = "bitmap" start_x = 52 start_y = 24 nxt-firmware-1.29.7/data/bitmaps/Test1.png000066400000000000000000000001131466344546000203220ustar00rootroot00000000000000PNG  IHDR@BIDATxc``@IENDB`nxt-firmware-1.29.7/data/bitmaps/Test1.toml000066400000000000000000000000521466344546000205130ustar00rootroot00000000000000format = "bitmap" start_x = 0 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/Test2.png000066400000000000000000000001221466344546000203230ustar00rootroot00000000000000PNG  IHDR@BIDATxcπ FFFFOL42IENDB`nxt-firmware-1.29.7/data/bitmaps/Test2.toml000066400000000000000000000000521466344546000205140ustar00rootroot00000000000000format = "bitmap" start_x = 0 start_y = 0 nxt-firmware-1.29.7/data/bitmaps/Wait.png000066400000000000000000000001611466344546000202310ustar00rootroot00000000000000PNG  IHDR U8IDATxu1*h1PBqܔ#n6.7O&b~> խ(dIENDB`nxt-firmware-1.29.7/data/bitmaps/Wait.toml000066400000000000000000000000521466344546000204170ustar00rootroot00000000000000format = "bitmap" start_x = 0 start_y = 8 nxt-firmware-1.29.7/data/icons/000077500000000000000000000000001466344546000162755ustar00rootroot00000000000000nxt-firmware-1.29.7/data/icons/Connections.png000066400000000000000000000002401466344546000212610ustar00rootroot00000000000000PNG  IHDR`/}gIDATx! @4z , X\44<0| q2fP#%g8{1G- j(~pEIIENDB`nxt-firmware-1.29.7/data/icons/Connections.toml000066400000000000000000000000661466344546000214560ustar00rootroot00000000000000format = "icon" item_pixels_x = 24 item_pixels_y = 24 nxt-firmware-1.29.7/data/icons/Devices.png000066400000000000000000000003461466344546000203700ustar00rootroot00000000000000PNG  IHDR`/}IDATx1ayh9DOrn!j l(^%s_>!Ns=F%}3Rzb?w %=6ļ"Ը aR.[BlA8"5"ֈT;R3Of)[VO2`AwX4IENDB`nxt-firmware-1.29.7/data/icons/Devices.toml000066400000000000000000000000661466344546000205560ustar00rootroot00000000000000format = "icon" item_pixels_x = 24 item_pixels_y = 24 nxt-firmware-1.29.7/data/icons/Font.png000066400000000000000000000010771466344546000177160ustar00rootroot00000000000000PNG  IHDR`0mBbIDATxMK@!6-锧R-&.tPA! 1c٥“ЩH֩dT kή`{^meu&SUr.yz5cGdhLxdKN?DtmAc?bi2z36Wy)h49P$YSYZ败T{;"|]g`nʍ&5xER{%-FӒ)U}zyN})LF/Fש$Xv4!.ØCg\  '}L@;&CRyG2hGW"UT~aT}1PsSA;G,c"`oaglG0 @:w:}hwB8EB|pN?+ȞNOb:D~-LϐU<[8ֵ*"蛫-bHF>ђ0٘Gn'ae ׺fϕcMYִp©$#YU^ewb՘y! "EF*Vuh^+b'\ףǺpn"~oT8m ]ܵN t|5v=;>NKϫ!4GEZ9~[NiQO+Ky7W(0( kpNh,&gⅉ</#yz/C7Xq*kvc~Mﺩ| ,˟gÕMiǗ҉;#D1@FkqQC*8n bC7>-!Cײ4gߺ~sq[\mΡC4Ñ7#u#sg1>"c|v,L?IQџ ֕# )ʻ Na.ۂ׸2\@ PR &d]Km1'É&K*<[;a 3-X" sj&G<)nn]*%bULyٮ w%|8I=Zz@w'o:nֻWRΗ,`GHD&t~ﴂ /Lq''2F" _Y; s RݗUEBx9}N5he9ܺyᶲuV A8Vx>A +"ZP4|{U4/Fb3*r/2PX*"At^kV5qf\ \a'.G$2{7- mo,kȒˇ+\d/s bmaܙkUWgg''L܍ Y?.g|Qw,;.ml] ](jWu7Dn}Elo{Kh!їѭkh|ofNtnc>'{i]+HA}zJR}m_As9>RO݃13mt%K e]EqPCyф5l-ӈscX VGي͑PХg~@/q91Vg<уXB.&F*)ZR,.{[x% PHd];w_MDYzM>{W?D4Z/V:<1\ş!s3WNT 9 ԕSSFU@4fL]DM`[)"Hd9鋰So:9|rq;޹j~[+͵?yuΌ/PPb93N{J tV5}36]Eu,ZBRF-->{Ш8A/\ -g7)v9+UųQB_rk +~2{̏-W(쮗x9_bfLV m*s>f ͨ;ǭdo⡁``9S灼IjH'Q13vfO&RRB3ٓ$&lq gTR,<6.mO,e<{:Ϟo?aPFfKosGXF֙Zm씼Xg{bѻ#YauHLUDfŗXXxa@IENDB`nxt-firmware-1.29.7/data/icons/Icons.toml000066400000000000000000000000661466344546000202470ustar00rootroot00000000000000format = "icon" item_pixels_x = 24 item_pixels_y = 24 nxt-firmware-1.29.7/data/icons/Port.png000066400000000000000000000001351466344546000177260ustar00rootroot00000000000000PNG  IHDR$IDATxc,[`tc_7jD7 %IENDB`nxt-firmware-1.29.7/data/icons/Port.toml000066400000000000000000000000641466344546000201160ustar00rootroot00000000000000format = "icon" item_pixels_x = 3 item_pixels_y = 8 nxt-firmware-1.29.7/data/icons/Running.png000066400000000000000000000004221466344546000204210ustar00rootroot00000000000000PNG  IHDRNIDATx10EnLT#E01laZ(!dRY)FBǟAFAH'`'Ų}u"u\_4|B߁P(߶}ϟ.QzH[?ٟ|]MDLqWǟD"dt wI s'W9HOi>vJ~c!IENDB`nxt-firmware-1.29.7/data/icons/Running.toml000066400000000000000000000000661466344546000206140ustar00rootroot00000000000000format = "icon" item_pixels_x = 24 item_pixels_y = 24 nxt-firmware-1.29.7/data/icons/Status.png000066400000000000000000000004451466344546000202710ustar00rootroot00000000000000PNG  IHDRH0FIDATxNPsR5AVq.$l$>`8iB؜5{$mHmg:|9\0''^;"C<@1dRp&-s-ZG3Z}9G@>+Z8CV mӰg`3@ww9N{z:oͳy6zbRc6':Ϫxo_K"rvq, &IENDB`nxt-firmware-1.29.7/data/icons/Status.toml000066400000000000000000000000651466344546000204560ustar00rootroot00000000000000format = "icon" item_pixels_x = 12 item_pixels_y = 8 nxt-firmware-1.29.7/data/icons/Step.png000066400000000000000000000005611466344546000177200ustar00rootroot00000000000000PNG  IHDRX@ko8IDATxJQϝ$ !f<`cBFK } ;.V"1@ !b6QX$7f9@*P֧q%KB<&yvQvyF*dL+08y\Zdʄi2?jMvΠ-t|v< i*{H-@+ld`<lևN731==n_  AUVm5AٲO*CM)TDDD}Gɂ2u&< oo腟օ gs@i촳 ҂[KIENDB`nxt-firmware-1.29.7/data/icons/Step.toml000066400000000000000000000000661466344546000201070ustar00rootroot00000000000000format = "icon" item_pixels_x = 11 item_pixels_y = 16 nxt-firmware-1.29.7/data/menus/000077500000000000000000000000001466344546000163115ustar00rootroot00000000000000nxt-firmware-1.29.7/data/menus/Mainmenu.toml000066400000000000000000000033161466344546000207620ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 2 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Turn off?" icon_image_no = 49 [items.flags] skip_this_mother_id = 1 enter_only_calls = true exit_load_menu = true [[items]] item_id = 2 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Turn off?" icon_image_no = 48 [items.flags] skip_this_mother_id = 1 exit_load_menu = true [[items]] item_id = 17 function_index = 0 function_parameter = 0 file_load_no = 1 next_menu = 1 icon_text = "My Files" icon_image_no = 59 [items.flags] exit_load_pointer = 1 [[items]] item_id = 33 function_index = 0 function_parameter = 0 file_load_no = 2 next_menu = 1 icon_text = "NXT Program" icon_image_no = 60 [items.flags] exit_load_pointer = 1 [[items]] item_id = 49 function_index = 10 function_parameter = 0 file_load_no = 3 next_menu = 1 icon_text = "NXT Datalog" icon_image_no = 61 [items.flags] exit_load_pointer = 1 only_datalog_enabled = true [[items]] item_id = 65 function_index = 14 function_parameter = 0 file_load_no = 4 next_menu = 1 icon_text = "View" icon_image_no = 62 [items.flags] exit_load_pointer = 1 [[items]] item_id = 81 function_index = 0 function_parameter = 0 file_load_no = 7 next_menu = 2 icon_text = "Bluetooth" icon_image_no = 81 [items.flags] exit_load_pointer = 1 [[items]] item_id = 97 function_index = 0 function_parameter = 0 file_load_no = 5 next_menu = 1 icon_text = "Settings" icon_image_no = 63 [items.flags] exit_load_pointer = 1 [[items]] item_id = 113 function_index = 0 function_parameter = 0 file_load_no = 6 next_menu = 1 icon_text = "Try Me" icon_image_no = 64 [items.flags] exit_load_pointer = 1 nxt-firmware-1.29.7/data/menus/Submenu01.toml000066400000000000000000000062641466344546000207750ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 6 function_parameter = 2 file_load_no = 0 next_menu = 1 icon_text = "Software files" icon_image_no = 28 [items.flags] skip_this_mother_id = 1 accept_incoming_request = true [[items]] item_id = 2 function_index = 6 function_parameter = 3 file_load_no = 0 next_menu = 1 icon_text = "NXT files" icon_image_no = 29 [items.flags] skip_this_mother_id = 1 accept_incoming_request = true [[items]] item_id = 3 function_index = 6 function_parameter = 1 file_load_no = 0 next_menu = 1 icon_text = "Sound files" icon_image_no = 27 [items.flags] skip_this_mother_id = 1 accept_incoming_request = true [[items]] item_id = 4 function_index = 6 function_parameter = 5 file_load_no = 0 next_menu = 2 icon_text = "Datalog files" icon_image_no = 31 [items.flags] accept_incoming_request = true only_datalog_enabled = true [[items]] item_id = 17 function_index = 6 function_parameter = 242 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 0 [items.flags] init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 20 function_index = 6 function_parameter = 242 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 0 [items.flags] init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 273 function_index = 8 function_parameter = 248 file_load_no = 0 next_menu = 0 icon_text = "Run" icon_image_no = 50 [items.flags] leave_background = true init_calls_with_0 = true [[items]] item_id = 529 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Send" icon_image_no = 51 [items.flags] only_bt_on = true [[items]] item_id = 785 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 2 icon_text = "Delete" icon_image_no = 52 [[items]] item_id = 276 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 2 icon_text = "Delete" icon_image_no = 52 [[items]] item_id = 532 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Send" icon_image_no = 51 [items.flags] only_bt_on = true [[items]] item_id = 4625 function_index = 16 function_parameter = 249 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 0 [items.flags] init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 4881 function_index = 9 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 49 [items.flags] back_twice = true [[items]] item_id = 8977 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 48 [items.flags] enter_act_as_exit = true [[items]] item_id = 4372 function_index = 9 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 49 [items.flags] back_twice = true [[items]] item_id = 8468 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 48 [items.flags] enter_act_as_exit = true [[items]] item_id = 4628 function_index = 16 function_parameter = 249 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 0 [items.flags] init_calls_with_0 = true left_right_as_call = true nxt-firmware-1.29.7/data/menus/Submenu02.toml000066400000000000000000000312111466344546000207640ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 11 function_parameter = 247 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 0 [items.flags] init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 17 function_index = 11 function_parameter = 35 file_load_no = 0 next_menu = 1 icon_text = "Forward 5" icon_image_no = 35 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 33 function_index = 11 function_parameter = 34 file_load_no = 0 next_menu = 1 icon_text = "Forward" icon_image_no = 34 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 49 function_index = 11 function_parameter = 41 file_load_no = 0 next_menu = 1 icon_text = "Turn right 2" icon_image_no = 41 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 65 function_index = 11 function_parameter = 40 file_load_no = 0 next_menu = 1 icon_text = "Turn right" icon_image_no = 40 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 81 function_index = 11 function_parameter = 47 file_load_no = 0 next_menu = 1 icon_text = "Back right 2" icon_image_no = 47 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 97 function_index = 11 function_parameter = 39 file_load_no = 0 next_menu = 1 icon_text = "Back right" icon_image_no = 39 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 113 function_index = 11 function_parameter = 43 file_load_no = 0 next_menu = 1 icon_text = "Tone 1" icon_image_no = 43 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 129 function_index = 11 function_parameter = 44 file_load_no = 0 next_menu = 1 icon_text = "Tone 2" icon_image_no = 44 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 145 function_index = 11 function_parameter = 36 file_load_no = 0 next_menu = 1 icon_text = "Back left 2" icon_image_no = 36 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 161 function_index = 11 function_parameter = 42 file_load_no = 0 next_menu = 1 icon_text = "Back left" icon_image_no = 42 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 177 function_index = 11 function_parameter = 37 file_load_no = 0 next_menu = 1 icon_text = "Turn left" icon_image_no = 37 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 193 function_index = 11 function_parameter = 38 file_load_no = 0 next_menu = 1 icon_text = "Turn left 2" icon_image_no = 38 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 209 function_index = 11 function_parameter = 33 file_load_no = 0 next_menu = 1 icon_text = "Empty" icon_image_no = 33 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 225 function_index = 11 function_parameter = 45 file_load_no = 0 next_menu = 1 icon_text = "Backward" icon_image_no = 45 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 241 function_index = 11 function_parameter = 46 file_load_no = 0 next_menu = 1 icon_text = "Backward 5" icon_image_no = 46 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 273 function_index = 11 function_parameter = 65 file_load_no = 0 next_menu = 1 icon_text = "Empty" icon_image_no = 33 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 529 function_index = 11 function_parameter = 70 file_load_no = 0 next_menu = 1 icon_text = "Wait 2" icon_image_no = 70 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 785 function_index = 11 function_parameter = 71 file_load_no = 0 next_menu = 1 icon_text = "Wait 5" icon_image_no = 71 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 1041 function_index = 11 function_parameter = 72 file_load_no = 0 next_menu = 1 icon_text = "Wait 10" icon_image_no = 72 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 1297 function_index = 11 function_parameter = 67 file_load_no = 0 next_menu = 1 icon_text = "Object" icon_image_no = 67 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 1553 function_index = 11 function_parameter = 68 file_load_no = 0 next_menu = 1 icon_text = "Sound" icon_image_no = 68 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 1809 function_index = 11 function_parameter = 66 file_load_no = 0 next_menu = 1 icon_text = "Light" icon_image_no = 66 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 2065 function_index = 11 function_parameter = 73 file_load_no = 0 next_menu = 1 icon_text = "Dark" icon_image_no = 73 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 2321 function_index = 11 function_parameter = 69 file_load_no = 0 next_menu = 1 icon_text = "Touch" icon_image_no = 69 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 4369 function_index = 11 function_parameter = 35 file_load_no = 0 next_menu = 1 icon_text = "Forward 5" icon_image_no = 35 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 8465 function_index = 11 function_parameter = 34 file_load_no = 0 next_menu = 1 icon_text = "Forward" icon_image_no = 34 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 12561 function_index = 11 function_parameter = 41 file_load_no = 0 next_menu = 1 icon_text = "Turn right 2" icon_image_no = 41 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 16657 function_index = 11 function_parameter = 40 file_load_no = 0 next_menu = 1 icon_text = "Turn right" icon_image_no = 40 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 20753 function_index = 11 function_parameter = 47 file_load_no = 0 next_menu = 1 icon_text = "Back right 2" icon_image_no = 47 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 24849 function_index = 11 function_parameter = 39 file_load_no = 0 next_menu = 1 icon_text = "Back right" icon_image_no = 39 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 28945 function_index = 11 function_parameter = 43 file_load_no = 0 next_menu = 1 icon_text = "Tone 1" icon_image_no = 43 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 33041 function_index = 11 function_parameter = 44 file_load_no = 0 next_menu = 1 icon_text = "Tone 2" icon_image_no = 44 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 37137 function_index = 11 function_parameter = 36 file_load_no = 0 next_menu = 1 icon_text = "Back left 2" icon_image_no = 36 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 41233 function_index = 11 function_parameter = 42 file_load_no = 0 next_menu = 1 icon_text = "Back left" icon_image_no = 42 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 45329 function_index = 11 function_parameter = 37 file_load_no = 0 next_menu = 1 icon_text = "Turn left" icon_image_no = 37 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 49425 function_index = 11 function_parameter = 38 file_load_no = 0 next_menu = 1 icon_text = "Turn left 2" icon_image_no = 38 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 53521 function_index = 11 function_parameter = 33 file_load_no = 0 next_menu = 1 icon_text = "Empty" icon_image_no = 33 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 57617 function_index = 11 function_parameter = 45 file_load_no = 0 next_menu = 1 icon_text = "Backward" icon_image_no = 45 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 61713 function_index = 11 function_parameter = 46 file_load_no = 0 next_menu = 1 icon_text = "Backward 5" icon_image_no = 46 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 69905 function_index = 11 function_parameter = 65 file_load_no = 0 next_menu = 1 icon_text = "Empty" icon_image_no = 33 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 135441 function_index = 11 function_parameter = 70 file_load_no = 0 next_menu = 1 icon_text = "Wait 2" icon_image_no = 70 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 200977 function_index = 11 function_parameter = 71 file_load_no = 0 next_menu = 1 icon_text = "Wait 5" icon_image_no = 71 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 266513 function_index = 11 function_parameter = 72 file_load_no = 0 next_menu = 1 icon_text = "Wait 10" icon_image_no = 72 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 332049 function_index = 11 function_parameter = 67 file_load_no = 0 next_menu = 1 icon_text = "Object" icon_image_no = 67 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 397585 function_index = 11 function_parameter = 68 file_load_no = 0 next_menu = 1 icon_text = "Sound" icon_image_no = 68 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 463121 function_index = 11 function_parameter = 66 file_load_no = 0 next_menu = 1 icon_text = "Light" icon_image_no = 66 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 528657 function_index = 11 function_parameter = 73 file_load_no = 0 next_menu = 1 icon_text = "Dark" icon_image_no = 73 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 594193 function_index = 11 function_parameter = 69 file_load_no = 0 next_menu = 1 icon_text = "Touch" icon_image_no = 69 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 1118481 function_index = 11 function_parameter = 251 file_load_no = 0 next_menu = 1 icon_text = "Stop" icon_image_no = 77 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 2167057 function_index = 11 function_parameter = 252 file_load_no = 0 next_menu = 1 icon_text = "Loop" icon_image_no = 78 [items.flags] skip_this_mother_id = 1 leave_background = true exit_calls_with_ff = true [[items]] item_id = 17895697 function_index = 11 function_parameter = 248 file_load_no = 0 next_menu = 0 icon_text = "Run" icon_image_no = 50 [items.flags] leave_background = true exit_calls_with_ff = true [[items]] item_id = 34672913 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Main menu" icon_image_no = 55 [items.flags] leave_background = true exit_calls_with_ff = true enter_leaves_menufile = true [[items]] item_id = 68227345 function_index = 11 function_parameter = 250 file_load_no = 0 next_menu = 2 icon_text = "Save" icon_image_no = 29 [items.flags] leave_background = true exit_calls_with_ff = true [[items]] item_id = 336662801 function_index = 11 function_parameter = 237 file_load_no = 0 next_menu = 0 icon_text = "Yes" icon_image_no = 49 [items.flags] leave_background = true enter_leaves_menufile = true [[items]] item_id = 605098257 function_index = 11 function_parameter = 246 file_load_no = 0 next_menu = 0 icon_text = "No" icon_image_no = 48 [items.flags] enter_act_as_exit = true leave_background = true exit_calls = true nxt-firmware-1.29.7/data/menus/Submenu03.toml000066400000000000000000000146121466344546000207730ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 10 function_parameter = 11 file_load_no = 0 next_menu = 1 icon_text = "Temperature `C" icon_image_no = 15 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 2 function_index = 10 function_parameter = 12 file_load_no = 0 next_menu = 1 icon_text = "Temperature `F" icon_image_no = 16 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 3 function_index = 10 function_parameter = 2 file_load_no = 0 next_menu = 1 icon_text = "Sound dB" icon_image_no = 2 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 4 function_index = 10 function_parameter = 3 file_load_no = 0 next_menu = 1 icon_text = "Sound dBA" icon_image_no = 3 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 5 function_index = 10 function_parameter = 4 file_load_no = 0 next_menu = 1 icon_text = "Reflected light" icon_image_no = 4 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 6 function_index = 10 function_parameter = 5 file_load_no = 0 next_menu = 1 icon_text = "Ambient light" icon_image_no = 5 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 7 function_index = 10 function_parameter = 8 file_load_no = 0 next_menu = 1 icon_text = "Motor Rotations" icon_image_no = 9 [items.flags] leave_background = true [[items]] item_id = 8 function_index = 10 function_parameter = 7 file_load_no = 0 next_menu = 1 icon_text = "Motor Degrees" icon_image_no = 8 [items.flags] leave_background = true [[items]] item_id = 9 function_index = 10 function_parameter = 6 file_load_no = 0 next_menu = 1 icon_text = "Touch" icon_image_no = 7 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 10 function_index = 10 function_parameter = 9 file_load_no = 0 next_menu = 1 icon_text = "UltraSonic inch" icon_image_no = 11 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 11 function_index = 10 function_parameter = 10 file_load_no = 0 next_menu = 1 icon_text = "UltraSonic cm" icon_image_no = 12 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 12 function_index = 10 function_parameter = 13 file_load_no = 0 next_menu = 1 icon_text = "Color" icon_image_no = 17 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 13 function_index = 10 function_parameter = 238 file_load_no = 0 next_menu = 1 icon_text = "Done" icon_image_no = 49 [items.flags] leave_background = true [[items]] item_id = 17 function_index = 10 function_parameter = 18 file_load_no = 0 next_menu = 1 icon_text = "Port 1" icon_image_no = 18 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 33 function_index = 10 function_parameter = 19 file_load_no = 0 next_menu = 1 icon_text = "Port 2" icon_image_no = 19 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 49 function_index = 10 function_parameter = 20 file_load_no = 0 next_menu = 1 icon_text = "Port 3" icon_image_no = 20 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 65 function_index = 10 function_parameter = 21 file_load_no = 0 next_menu = 1 icon_text = "Port 4" icon_image_no = 21 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 23 function_index = 10 function_parameter = 22 file_load_no = 0 next_menu = 1 icon_text = "Port A" icon_image_no = 22 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 39 function_index = 10 function_parameter = 23 file_load_no = 0 next_menu = 1 icon_text = "Port B" icon_image_no = 23 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 55 function_index = 10 function_parameter = 24 file_load_no = 0 next_menu = 1 icon_text = "Port C" icon_image_no = 24 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 24 function_index = 10 function_parameter = 22 file_load_no = 0 next_menu = 1 icon_text = "Port A" icon_image_no = 22 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 40 function_index = 10 function_parameter = 23 file_load_no = 0 next_menu = 1 icon_text = "Port B" icon_image_no = 23 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 56 function_index = 10 function_parameter = 24 file_load_no = 0 next_menu = 1 icon_text = "Port C" icon_image_no = 23 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 29 function_index = 10 function_parameter = 247 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 0 [items.flags] auto_press_enter = true [[items]] item_id = 273 function_index = 10 function_parameter = 242 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 0 [items.flags] auto_press_enter = true back_three_times = true exit_load_pointer = 13 [[items]] item_id = 279 function_index = 10 function_parameter = 242 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 0 [items.flags] auto_press_enter = true back_three_times = true exit_load_pointer = 13 [[items]] item_id = 280 function_index = 10 function_parameter = 242 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 0 [items.flags] auto_press_enter = true back_three_times = true exit_load_pointer = 13 [[items]] item_id = 285 function_index = 10 function_parameter = 248 file_load_no = 0 next_menu = 2 icon_text = "Run" icon_image_no = 50 [items.flags] back_twice = true leave_background = true exit_calls_with_ff = true [[items]] item_id = 4381 function_index = 10 function_parameter = 241 file_load_no = 0 next_menu = 0 icon_text = "Main menu" icon_image_no = 55 [items.flags] enter_leaves_menufile = true exit_disable = true [[items]] item_id = 8477 function_index = 10 function_parameter = 250 file_load_no = 0 next_menu = 2 icon_text = "Save" icon_image_no = 31 [items.flags] exit_disable = true [[items]] item_id = 74013 function_index = 10 function_parameter = 237 file_load_no = 0 next_menu = 0 icon_text = "Yes" icon_image_no = 49 [items.flags] leave_background = true enter_leaves_menufile = true [[items]] item_id = 139549 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "No" icon_image_no = 48 [items.flags] enter_act_as_exit = true leave_background = true exit_calls = true nxt-firmware-1.29.7/data/menus/Submenu04.toml000066400000000000000000000102661466344546000207750ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 14 function_parameter = 2 file_load_no = 0 next_menu = 1 icon_text = "Sound dB" icon_image_no = 2 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 2 function_index = 14 function_parameter = 3 file_load_no = 0 next_menu = 1 icon_text = "Sound dBA" icon_image_no = 3 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 3 function_index = 14 function_parameter = 4 file_load_no = 0 next_menu = 1 icon_text = "Reflected light" icon_image_no = 4 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 4 function_index = 14 function_parameter = 5 file_load_no = 0 next_menu = 1 icon_text = "Ambient light" icon_image_no = 5 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 5 function_index = 14 function_parameter = 11 file_load_no = 0 next_menu = 1 icon_text = "Temperature `C" icon_image_no = 15 [items.flags] skip_this_mother_id = 1 leave_background = true init_calls_with_0 = true [[items]] item_id = 6 function_index = 14 function_parameter = 12 file_load_no = 0 next_menu = 1 icon_text = "Temperature `F" icon_image_no = 16 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 7 function_index = 14 function_parameter = 8 file_load_no = 0 next_menu = 1 icon_text = "Motor rotations" icon_image_no = 9 [items.flags] leave_background = true [[items]] item_id = 8 function_index = 14 function_parameter = 7 file_load_no = 0 next_menu = 1 icon_text = "Motor degrees" icon_image_no = 8 [items.flags] leave_background = true [[items]] item_id = 9 function_index = 14 function_parameter = 6 file_load_no = 0 next_menu = 1 icon_text = "Touch" icon_image_no = 7 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 10 function_index = 14 function_parameter = 9 file_load_no = 0 next_menu = 1 icon_text = "Ultrasonic inch" icon_image_no = 11 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 11 function_index = 14 function_parameter = 10 file_load_no = 0 next_menu = 1 icon_text = "Ultrasonic cm" icon_image_no = 12 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 12 function_index = 14 function_parameter = 13 file_load_no = 0 next_menu = 1 icon_text = "Color" icon_image_no = 17 [items.flags] skip_this_mother_id = 1 leave_background = true [[items]] item_id = 17 function_index = 14 function_parameter = 18 file_load_no = 0 next_menu = 0 icon_text = "Port 1" icon_image_no = 18 [items.flags] leave_background = true [[items]] item_id = 33 function_index = 14 function_parameter = 19 file_load_no = 0 next_menu = 0 icon_text = "Port 2" icon_image_no = 19 [items.flags] leave_background = true [[items]] item_id = 49 function_index = 14 function_parameter = 20 file_load_no = 0 next_menu = 0 icon_text = "Port 3" icon_image_no = 20 [items.flags] leave_background = true [[items]] item_id = 65 function_index = 14 function_parameter = 21 file_load_no = 0 next_menu = 0 icon_text = "Port 4" icon_image_no = 21 [items.flags] leave_background = true [[items]] item_id = 23 function_index = 14 function_parameter = 22 file_load_no = 0 next_menu = 0 icon_text = "Port A" icon_image_no = 22 [items.flags] leave_background = true [[items]] item_id = 39 function_index = 14 function_parameter = 23 file_load_no = 0 next_menu = 0 icon_text = "Port B" icon_image_no = 23 [items.flags] leave_background = true [[items]] item_id = 55 function_index = 14 function_parameter = 24 file_load_no = 0 next_menu = 0 icon_text = "Port C" icon_image_no = 24 [items.flags] leave_background = true [[items]] item_id = 24 function_index = 14 function_parameter = 22 file_load_no = 0 next_menu = 0 icon_text = "Port A" icon_image_no = 22 [items.flags] leave_background = true [[items]] item_id = 40 function_index = 14 function_parameter = 23 file_load_no = 0 next_menu = 0 icon_text = "Port B" icon_image_no = 23 [items.flags] leave_background = true [[items]] item_id = 56 function_index = 14 function_parameter = 24 file_load_no = 0 next_menu = 0 icon_text = "Port C" icon_image_no = 24 [items.flags] leave_background = true nxt-firmware-1.29.7/data/menus/Submenu05.toml000066400000000000000000000060341466344546000207740ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Volume" icon_image_no = 57 [items.flags] accept_incoming_request = true [[items]] item_id = 2 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Sleep" icon_image_no = 58 [items.flags] accept_incoming_request = true [[items]] item_id = 3 function_index = 1 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "NXT Version" icon_image_no = 79 [[items]] item_id = 4 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Delete files" icon_image_no = 52 [items.flags] accept_incoming_request = true [[items]] item_id = 17 function_index = 7 function_parameter = 239 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 57 [items.flags] leave_background = true exit_calls_with_ff = true init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 33 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 53 [[items]] item_id = 49 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 54 [[items]] item_id = 18 function_index = 4 function_parameter = 239 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 58 [items.flags] leave_background = true init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 34 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 53 [[items]] item_id = 50 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 54 [[items]] item_id = 20 function_index = 5 function_parameter = 2 file_load_no = 0 next_menu = 2 icon_text = "Software files" icon_image_no = 28 [items.flags] skip_this_mother_id = 1 [[items]] item_id = 36 function_index = 5 function_parameter = 3 file_load_no = 0 next_menu = 2 icon_text = "NXT files" icon_image_no = 29 [items.flags] skip_this_mother_id = 1 [[items]] item_id = 52 function_index = 5 function_parameter = 1 file_load_no = 0 next_menu = 2 icon_text = "Sound files" icon_image_no = 27 [items.flags] skip_this_mother_id = 1 [[items]] item_id = 68 function_index = 5 function_parameter = 5 file_load_no = 0 next_menu = 2 icon_text = "Datalog files" icon_image_no = 31 [items.flags] skip_this_mother_id = 1 only_datalog_enabled = true [[items]] item_id = 84 function_index = 5 function_parameter = 4 file_load_no = 0 next_menu = 2 icon_text = "Try me files" icon_image_no = 30 [items.flags] skip_this_mother_id = 1 [[items]] item_id = 276 function_index = 5 function_parameter = 241 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 49 [items.flags] back_twice = true init_calls_with_0 = true [[items]] item_id = 532 function_index = 5 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 48 [items.flags] enter_act_as_exit = true init_calls_with_0 = true nxt-firmware-1.29.7/data/menus/Submenu06.toml000066400000000000000000000022041466344546000207700ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 6 function_parameter = 4 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 30 [items.flags] auto_press_enter = true [[items]] item_id = 17 function_index = 6 function_parameter = 242 file_load_no = 0 next_menu = 2 icon_text = " " icon_image_no = 30 [items.flags] exit_leaves_menufile = true init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 273 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 2 icon_text = "Delete" icon_image_no = 52 [[items]] item_id = 529 function_index = 8 function_parameter = 248 file_load_no = 0 next_menu = 0 icon_text = "Run" icon_image_no = 50 [items.flags] leave_background = true init_calls_with_0 = true [[items]] item_id = 4369 function_index = 9 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 49 [items.flags] back_twice = true [[items]] item_id = 8465 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 0 icon_text = "Are you sure?" icon_image_no = 48 [items.flags] enter_act_as_exit = true nxt-firmware-1.29.7/data/menus/Submenu07.toml000066400000000000000000000067661466344546000210120ustar00rootroot00000000000000format = "menu" item_pixels_x = 24 item_pixels_y = 24 [[items]] item_id = 1 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Search" icon_image_no = 86 [items.flags] accept_incoming_request = true only_bt_on = true [[items]] item_id = 2 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "My contacts" icon_image_no = 82 [items.flags] accept_incoming_request = true only_bt_on = true [[items]] item_id = 3 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Connections" icon_image_no = 83 [items.flags] accept_incoming_request = true only_bt_on = true [[items]] item_id = 4 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Visibility" icon_image_no = 84 [items.flags] accept_incoming_request = true only_bt_on = true [[items]] item_id = 5 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "On/Off" icon_image_no = 85 [items.flags] accept_incoming_request = true [[items]] item_id = 17 function_index = 18 function_parameter = 255 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 87 [items.flags] init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 18 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 87 [items.flags] auto_press_enter = true [[items]] item_id = 19 function_index = 20 function_parameter = 246 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 87 [items.flags] init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 20 function_index = 17 function_parameter = 235 file_load_no = 0 next_menu = 0 icon_text = "Visible" icon_image_no = 90 [[items]] item_id = 36 function_index = 17 function_parameter = 234 file_load_no = 0 next_menu = 0 icon_text = "Invisible" icon_image_no = 91 [[items]] item_id = 21 function_index = 3 function_parameter = 235 file_load_no = 0 next_menu = 0 icon_text = "On" icon_image_no = 92 [items.flags] exit_leaves_menufile = true [[items]] item_id = 37 function_index = 3 function_parameter = 234 file_load_no = 0 next_menu = 0 icon_text = "Off" icon_image_no = 93 [items.flags] exit_leaves_menufile = true only_bt_on = true [[items]] item_id = 273 function_index = 19 function_parameter = 242 file_load_no = 0 next_menu = 1 icon_text = " " icon_image_no = 87 [items.flags] back_twice = true init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 274 function_index = 19 function_parameter = 242 file_load_no = 0 next_menu = 2 icon_text = " " icon_image_no = 87 [items.flags] back_twice = true left_right_as_call = true init_calls_with_1 = true [[items]] item_id = 275 function_index = 20 function_parameter = 240 file_load_no = 0 next_menu = 0 icon_text = "Disconnect" icon_image_no = 89 [[items]] item_id = 4369 function_index = 16 function_parameter = 245 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 87 [items.flags] init_calls_with_0 = true left_right_as_call = true [[items]] item_id = 4370 function_index = 19 function_parameter = 241 file_load_no = 0 next_menu = 0 icon_text = "Delete" icon_image_no = 52 [[items]] item_id = 8466 function_index = 0 function_parameter = 0 file_load_no = 0 next_menu = 1 icon_text = "Connect" icon_image_no = 88 [[items]] item_id = 74002 function_index = 16 function_parameter = 245 file_load_no = 0 next_menu = 0 icon_text = " " icon_image_no = 87 [items.flags] back_twice = true init_calls_with_0 = true left_right_as_call = true nxt-firmware-1.29.7/include/000077500000000000000000000000001466344546000156745ustar00rootroot00000000000000nxt-firmware-1.29.7/include/AT91SAM7S256.h000066400000000000000000004342561466344546000175510ustar00rootroot00000000000000// ---------------------------------------------------------------------------- // ATMEL Microcontroller Software Support - ROUSSET - // ---------------------------------------------------------------------------- // DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE // DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // ---------------------------------------------------------------------------- // File Name : AT91SAM7S256.h // Object : AT91SAM7S256 definitions // Generated : AT91 SW Application Group 03/08/2005 (15:46:13) // // CVS Reference : /AT91SAM7S256.pl/1.8/Wed Feb 9 15:29:26 2005// // CVS Reference : /SYS_SAM7S.pl/1.2/Tue Feb 1 17:01:52 2005// // CVS Reference : /MC_SAM7S.pl/1.2/Tue Feb 1 17:01:00 2005// // CVS Reference : /PMC_SAM7S_USB.pl/1.4/Tue Feb 8 13:58:22 2005// // CVS Reference : /RSTC_SAM7S.pl/1.1/Tue Feb 1 16:16:35 2005// // CVS Reference : /RTTC_6081A.pl/1.2/Tue Nov 9 14:43:58 2004// // CVS Reference : /PITC_6079A.pl/1.2/Tue Nov 9 14:43:56 2004// // CVS Reference : /WDTC_6080A.pl/1.3/Tue Nov 9 14:44:00 2004// // CVS Reference : /VREG_6085B.pl/1.1/Tue Feb 1 16:05:48 2005// // CVS Reference : /UDP_6083C.pl/1.1/Mon Jan 31 13:01:46 2005// // CVS Reference : /AIC_6075A.pl/1.1/Fri Jun 28 10:36:48 2002// // CVS Reference : /PIO_6057A.pl/1.2/Thu Feb 3 10:18:28 2005// // CVS Reference : /DBGU_6059D.pl/1.1/Mon Jan 31 13:15:32 2005// // CVS Reference : /US_6089C.pl/1.1/Mon Jul 12 18:23:26 2004// // CVS Reference : /SPI_6088D.pl/1.2/Mon Feb 14 07:24:18 2005// // CVS Reference : /SSC_6078A.pl/1.1/Tue Jul 13 07:45:40 2004// // CVS Reference : /TC_6082A.pl/1.6/Fri Feb 18 13:53:30 2005// // CVS Reference : /TWI_6061A.pl/1.1/Tue Jul 13 07:38:06 2004// // CVS Reference : /PDC_6074C.pl/1.2/Thu Feb 3 08:48:54 2005// // CVS Reference : /ADC_6051C.pl/1.1/Fri Oct 17 09:12:38 2003// // CVS Reference : /PWM_6044D.pl/1.1/Tue Apr 27 14:53:52 2004// // ---------------------------------------------------------------------------- #ifndef AT91SAM7S256_H #define AT91SAM7S256_H typedef volatile unsigned int AT91_REG;// Hardware register definition // ***************************************************************************** // SOFTWARE API DEFINITION FOR System Peripherals // ***************************************************************************** typedef struct _AT91S_SYS { AT91_REG AIC_SMR[32]; // Source Mode Register AT91_REG AIC_SVR[32]; // Source Vector Register AT91_REG AIC_IVR; // IRQ Vector Register AT91_REG AIC_FVR; // FIQ Vector Register AT91_REG AIC_ISR; // Interrupt Status Register AT91_REG AIC_IPR; // Interrupt Pending Register AT91_REG AIC_IMR; // Interrupt Mask Register AT91_REG AIC_CISR; // Core Interrupt Status Register AT91_REG Reserved0[2]; // AT91_REG AIC_IECR; // Interrupt Enable Command Register AT91_REG AIC_IDCR; // Interrupt Disable Command Register AT91_REG AIC_ICCR; // Interrupt Clear Command Register AT91_REG AIC_ISCR; // Interrupt Set Command Register AT91_REG AIC_EOICR; // End of Interrupt Command Register AT91_REG AIC_SPU; // Spurious Vector Register AT91_REG AIC_DCR; // Debug Control Register (Protect) AT91_REG Reserved1[1]; // AT91_REG AIC_FFER; // Fast Forcing Enable Register AT91_REG AIC_FFDR; // Fast Forcing Disable Register AT91_REG AIC_FFSR; // Fast Forcing Status Register AT91_REG Reserved2[45]; // AT91_REG DBGU_CR; // Control Register AT91_REG DBGU_MR; // Mode Register AT91_REG DBGU_IER; // Interrupt Enable Register AT91_REG DBGU_IDR; // Interrupt Disable Register AT91_REG DBGU_IMR; // Interrupt Mask Register AT91_REG DBGU_CSR; // Channel Status Register AT91_REG DBGU_RHR; // Receiver Holding Register AT91_REG DBGU_THR; // Transmitter Holding Register AT91_REG DBGU_BRGR; // Baud Rate Generator Register AT91_REG Reserved3[7]; // AT91_REG DBGU_CIDR; // Chip ID Register AT91_REG DBGU_EXID; // Chip ID Extension Register AT91_REG DBGU_FNTR; // Force NTRST Register AT91_REG Reserved4[45]; // AT91_REG DBGU_RPR; // Receive Pointer Register AT91_REG DBGU_RCR; // Receive Counter Register AT91_REG DBGU_TPR; // Transmit Pointer Register AT91_REG DBGU_TCR; // Transmit Counter Register AT91_REG DBGU_RNPR; // Receive Next Pointer Register AT91_REG DBGU_RNCR; // Receive Next Counter Register AT91_REG DBGU_TNPR; // Transmit Next Pointer Register AT91_REG DBGU_TNCR; // Transmit Next Counter Register AT91_REG DBGU_PTCR; // PDC Transfer Control Register AT91_REG DBGU_PTSR; // PDC Transfer Status Register AT91_REG Reserved5[54]; // AT91_REG PIOA_PER; // PIO Enable Register AT91_REG PIOA_PDR; // PIO Disable Register AT91_REG PIOA_PSR; // PIO Status Register AT91_REG Reserved6[1]; // AT91_REG PIOA_OER; // Output Enable Register AT91_REG PIOA_ODR; // Output Disable Registerr AT91_REG PIOA_OSR; // Output Status Register AT91_REG Reserved7[1]; // AT91_REG PIOA_IFER; // Input Filter Enable Register AT91_REG PIOA_IFDR; // Input Filter Disable Register AT91_REG PIOA_IFSR; // Input Filter Status Register AT91_REG Reserved8[1]; // AT91_REG PIOA_SODR; // Set Output Data Register AT91_REG PIOA_CODR; // Clear Output Data Register AT91_REG PIOA_ODSR; // Output Data Status Register AT91_REG PIOA_PDSR; // Pin Data Status Register AT91_REG PIOA_IER; // Interrupt Enable Register AT91_REG PIOA_IDR; // Interrupt Disable Register AT91_REG PIOA_IMR; // Interrupt Mask Register AT91_REG PIOA_ISR; // Interrupt Status Register AT91_REG PIOA_MDER; // Multi-driver Enable Register AT91_REG PIOA_MDDR; // Multi-driver Disable Register AT91_REG PIOA_MDSR; // Multi-driver Status Register AT91_REG Reserved9[1]; // AT91_REG PIOA_PPUDR; // Pull-up Disable Register AT91_REG PIOA_PPUER; // Pull-up Enable Register AT91_REG PIOA_PPUSR; // Pull-up Status Register AT91_REG Reserved10[1]; // AT91_REG PIOA_ASR; // Select A Register AT91_REG PIOA_BSR; // Select B Register AT91_REG PIOA_ABSR; // AB Select Status Register AT91_REG Reserved11[9]; // AT91_REG PIOA_OWER; // Output Write Enable Register AT91_REG PIOA_OWDR; // Output Write Disable Register AT91_REG PIOA_OWSR; // Output Write Status Register AT91_REG Reserved12[469]; // AT91_REG PMC_SCER; // System Clock Enable Register AT91_REG PMC_SCDR; // System Clock Disable Register AT91_REG PMC_SCSR; // System Clock Status Register AT91_REG Reserved13[1]; // AT91_REG PMC_PCER; // Peripheral Clock Enable Register AT91_REG PMC_PCDR; // Peripheral Clock Disable Register AT91_REG PMC_PCSR; // Peripheral Clock Status Register AT91_REG Reserved14[1]; // AT91_REG PMC_MOR; // Main Oscillator Register AT91_REG PMC_MCFR; // Main Clock Frequency Register AT91_REG Reserved15[1]; // AT91_REG PMC_PLLR; // PLL Register AT91_REG PMC_MCKR; // Master Clock Register AT91_REG Reserved16[3]; // AT91_REG PMC_PCKR[3]; // Programmable Clock Register AT91_REG Reserved17[5]; // AT91_REG PMC_IER; // Interrupt Enable Register AT91_REG PMC_IDR; // Interrupt Disable Register AT91_REG PMC_SR; // Status Register AT91_REG PMC_IMR; // Interrupt Mask Register AT91_REG Reserved18[36]; // AT91_REG RSTC_RCR; // Reset Control Register AT91_REG RSTC_RSR; // Reset Status Register AT91_REG RSTC_RMR; // Reset Mode Register AT91_REG Reserved19[5]; // AT91_REG RTTC_RTMR; // Real-time Mode Register AT91_REG RTTC_RTAR; // Real-time Alarm Register AT91_REG RTTC_RTVR; // Real-time Value Register AT91_REG RTTC_RTSR; // Real-time Status Register AT91_REG PITC_PIMR; // Period Interval Mode Register AT91_REG PITC_PISR; // Period Interval Status Register AT91_REG PITC_PIVR; // Period Interval Value Register AT91_REG PITC_PIIR; // Period Interval Image Register AT91_REG WDTC_WDCR; // Watchdog Control Register AT91_REG WDTC_WDMR; // Watchdog Mode Register AT91_REG WDTC_WDSR; // Watchdog Status Register AT91_REG Reserved20[5]; // AT91_REG VREG_MR; // Voltage Regulator Mode Register } AT91S_SYS, *AT91PS_SYS; // ***************************************************************************** // SOFTWARE API DEFINITION FOR Advanced Interrupt Controller // ***************************************************************************** typedef struct _AT91S_AIC { AT91_REG AIC_SMR[32]; // Source Mode Register AT91_REG AIC_SVR[32]; // Source Vector Register AT91_REG AIC_IVR; // IRQ Vector Register AT91_REG AIC_FVR; // FIQ Vector Register AT91_REG AIC_ISR; // Interrupt Status Register AT91_REG AIC_IPR; // Interrupt Pending Register AT91_REG AIC_IMR; // Interrupt Mask Register AT91_REG AIC_CISR; // Core Interrupt Status Register AT91_REG Reserved0[2]; // AT91_REG AIC_IECR; // Interrupt Enable Command Register AT91_REG AIC_IDCR; // Interrupt Disable Command Register AT91_REG AIC_ICCR; // Interrupt Clear Command Register AT91_REG AIC_ISCR; // Interrupt Set Command Register AT91_REG AIC_EOICR; // End of Interrupt Command Register AT91_REG AIC_SPU; // Spurious Vector Register AT91_REG AIC_DCR; // Debug Control Register (Protect) AT91_REG Reserved1[1]; // AT91_REG AIC_FFER; // Fast Forcing Enable Register AT91_REG AIC_FFDR; // Fast Forcing Disable Register AT91_REG AIC_FFSR; // Fast Forcing Status Register } AT91S_AIC, *AT91PS_AIC; // -------- AIC_SMR : (AIC Offset: 0x0) Control Register -------- #define AT91C_AIC_PRIOR ((unsigned int) 0x7 << 0) // (AIC) Priority Level #define AT91C_AIC_PRIOR_LOWEST ((unsigned int) 0x0) // (AIC) Lowest priority level #define AT91C_AIC_PRIOR_HIGHEST ((unsigned int) 0x7) // (AIC) Highest priority level #define AT91C_AIC_SRCTYPE ((unsigned int) 0x3 << 5) // (AIC) Interrupt Source Type #define AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE ((unsigned int) 0x0 << 5) // (AIC) Internal Sources Code Label Level Sensitive #define AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED ((unsigned int) 0x1 << 5) // (AIC) Internal Sources Code Label Edge triggered #define AT91C_AIC_SRCTYPE_EXT_HIGH_LEVEL ((unsigned int) 0x2 << 5) // (AIC) External Sources Code Label High-level Sensitive #define AT91C_AIC_SRCTYPE_EXT_POSITIVE_EDGE ((unsigned int) 0x3 << 5) // (AIC) External Sources Code Label Positive Edge triggered // -------- AIC_CISR : (AIC Offset: 0x114) AIC Core Interrupt Status Register -------- #define AT91C_AIC_NFIQ ((unsigned int) 0x1 << 0) // (AIC) NFIQ Status #define AT91C_AIC_NIRQ ((unsigned int) 0x1 << 1) // (AIC) NIRQ Status // -------- AIC_DCR : (AIC Offset: 0x138) AIC Debug Control Register (Protect) -------- #define AT91C_AIC_DCR_PROT ((unsigned int) 0x1 << 0) // (AIC) Protection Mode #define AT91C_AIC_DCR_GMSK ((unsigned int) 0x1 << 1) // (AIC) General Mask // ***************************************************************************** // SOFTWARE API DEFINITION FOR Peripheral DMA Controller // ***************************************************************************** typedef struct _AT91S_PDC { AT91_REG PDC_RPR; // Receive Pointer Register AT91_REG PDC_RCR; // Receive Counter Register AT91_REG PDC_TPR; // Transmit Pointer Register AT91_REG PDC_TCR; // Transmit Counter Register AT91_REG PDC_RNPR; // Receive Next Pointer Register AT91_REG PDC_RNCR; // Receive Next Counter Register AT91_REG PDC_TNPR; // Transmit Next Pointer Register AT91_REG PDC_TNCR; // Transmit Next Counter Register AT91_REG PDC_PTCR; // PDC Transfer Control Register AT91_REG PDC_PTSR; // PDC Transfer Status Register } AT91S_PDC, *AT91PS_PDC; // -------- PDC_PTCR : (PDC Offset: 0x20) PDC Transfer Control Register -------- #define AT91C_PDC_RXTEN ((unsigned int) 0x1 << 0) // (PDC) Receiver Transfer Enable #define AT91C_PDC_RXTDIS ((unsigned int) 0x1 << 1) // (PDC) Receiver Transfer Disable #define AT91C_PDC_TXTEN ((unsigned int) 0x1 << 8) // (PDC) Transmitter Transfer Enable #define AT91C_PDC_TXTDIS ((unsigned int) 0x1 << 9) // (PDC) Transmitter Transfer Disable // -------- PDC_PTSR : (PDC Offset: 0x24) PDC Transfer Status Register -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR Debug Unit // ***************************************************************************** typedef struct _AT91S_DBGU { AT91_REG DBGU_CR; // Control Register AT91_REG DBGU_MR; // Mode Register AT91_REG DBGU_IER; // Interrupt Enable Register AT91_REG DBGU_IDR; // Interrupt Disable Register AT91_REG DBGU_IMR; // Interrupt Mask Register AT91_REG DBGU_CSR; // Channel Status Register AT91_REG DBGU_RHR; // Receiver Holding Register AT91_REG DBGU_THR; // Transmitter Holding Register AT91_REG DBGU_BRGR; // Baud Rate Generator Register AT91_REG Reserved0[7]; // AT91_REG DBGU_CIDR; // Chip ID Register AT91_REG DBGU_EXID; // Chip ID Extension Register AT91_REG DBGU_FNTR; // Force NTRST Register AT91_REG Reserved1[45]; // AT91_REG DBGU_RPR; // Receive Pointer Register AT91_REG DBGU_RCR; // Receive Counter Register AT91_REG DBGU_TPR; // Transmit Pointer Register AT91_REG DBGU_TCR; // Transmit Counter Register AT91_REG DBGU_RNPR; // Receive Next Pointer Register AT91_REG DBGU_RNCR; // Receive Next Counter Register AT91_REG DBGU_TNPR; // Transmit Next Pointer Register AT91_REG DBGU_TNCR; // Transmit Next Counter Register AT91_REG DBGU_PTCR; // PDC Transfer Control Register AT91_REG DBGU_PTSR; // PDC Transfer Status Register } AT91S_DBGU, *AT91PS_DBGU; // -------- DBGU_CR : (DBGU Offset: 0x0) Debug Unit Control Register -------- #define AT91C_US_RSTRX ((unsigned int) 0x1 << 2) // (DBGU) Reset Receiver #define AT91C_US_RSTTX ((unsigned int) 0x1 << 3) // (DBGU) Reset Transmitter #define AT91C_US_RXEN ((unsigned int) 0x1 << 4) // (DBGU) Receiver Enable #define AT91C_US_RXDIS ((unsigned int) 0x1 << 5) // (DBGU) Receiver Disable #define AT91C_US_TXEN ((unsigned int) 0x1 << 6) // (DBGU) Transmitter Enable #define AT91C_US_TXDIS ((unsigned int) 0x1 << 7) // (DBGU) Transmitter Disable #define AT91C_US_RSTSTA ((unsigned int) 0x1 << 8) // (DBGU) Reset Status Bits // -------- DBGU_MR : (DBGU Offset: 0x4) Debug Unit Mode Register -------- #define AT91C_US_PAR ((unsigned int) 0x7 << 9) // (DBGU) Parity type #define AT91C_US_PAR_EVEN ((unsigned int) 0x0 << 9) // (DBGU) Even Parity #define AT91C_US_PAR_ODD ((unsigned int) 0x1 << 9) // (DBGU) Odd Parity #define AT91C_US_PAR_SPACE ((unsigned int) 0x2 << 9) // (DBGU) Parity forced to 0 (Space) #define AT91C_US_PAR_MARK ((unsigned int) 0x3 << 9) // (DBGU) Parity forced to 1 (Mark) #define AT91C_US_PAR_NONE ((unsigned int) 0x4 << 9) // (DBGU) No Parity #define AT91C_US_PAR_MULTI_DROP ((unsigned int) 0x6 << 9) // (DBGU) Multi-drop mode #define AT91C_US_CHMODE ((unsigned int) 0x3 << 14) // (DBGU) Channel Mode #define AT91C_US_CHMODE_NORMAL ((unsigned int) 0x0 << 14) // (DBGU) Normal Mode: The USART channel operates as an RX/TX USART. #define AT91C_US_CHMODE_AUTO ((unsigned int) 0x1 << 14) // (DBGU) Automatic Echo: Receiver Data Input is connected to the TXD pin. #define AT91C_US_CHMODE_LOCAL ((unsigned int) 0x2 << 14) // (DBGU) Local Loopback: Transmitter Output Signal is connected to Receiver Input Signal. #define AT91C_US_CHMODE_REMOTE ((unsigned int) 0x3 << 14) // (DBGU) Remote Loopback: RXD pin is internally connected to TXD pin. // -------- DBGU_IER : (DBGU Offset: 0x8) Debug Unit Interrupt Enable Register -------- #define AT91C_US_RXRDY ((unsigned int) 0x1 << 0) // (DBGU) RXRDY Interrupt #define AT91C_US_TXRDY ((unsigned int) 0x1 << 1) // (DBGU) TXRDY Interrupt #define AT91C_US_ENDRX ((unsigned int) 0x1 << 3) // (DBGU) End of Receive Transfer Interrupt #define AT91C_US_ENDTX ((unsigned int) 0x1 << 4) // (DBGU) End of Transmit Interrupt #define AT91C_US_OVRE ((unsigned int) 0x1 << 5) // (DBGU) Overrun Interrupt #define AT91C_US_FRAME ((unsigned int) 0x1 << 6) // (DBGU) Framing Error Interrupt #define AT91C_US_PARE ((unsigned int) 0x1 << 7) // (DBGU) Parity Error Interrupt #define AT91C_US_TXEMPTY ((unsigned int) 0x1 << 9) // (DBGU) TXEMPTY Interrupt #define AT91C_US_TXBUFE ((unsigned int) 0x1 << 11) // (DBGU) TXBUFE Interrupt #define AT91C_US_RXBUFF ((unsigned int) 0x1 << 12) // (DBGU) RXBUFF Interrupt #define AT91C_US_COMM_TX ((unsigned int) 0x1 << 30) // (DBGU) COMM_TX Interrupt #define AT91C_US_COMM_RX ((unsigned int) 0x1 << 31) // (DBGU) COMM_RX Interrupt // -------- DBGU_IDR : (DBGU Offset: 0xc) Debug Unit Interrupt Disable Register -------- // -------- DBGU_IMR : (DBGU Offset: 0x10) Debug Unit Interrupt Mask Register -------- // -------- DBGU_CSR : (DBGU Offset: 0x14) Debug Unit Channel Status Register -------- // -------- DBGU_FNTR : (DBGU Offset: 0x48) Debug Unit FORCE_NTRST Register -------- #define AT91C_US_FORCE_NTRST ((unsigned int) 0x1 << 0) // (DBGU) Force NTRST in JTAG // ***************************************************************************** // SOFTWARE API DEFINITION FOR Parallel Input Output Controler // ***************************************************************************** typedef struct _AT91S_PIO { AT91_REG PIO_PER; // PIO Enable Register AT91_REG PIO_PDR; // PIO Disable Register AT91_REG PIO_PSR; // PIO Status Register AT91_REG Reserved0[1]; // AT91_REG PIO_OER; // Output Enable Register AT91_REG PIO_ODR; // Output Disable Registerr AT91_REG PIO_OSR; // Output Status Register AT91_REG Reserved1[1]; // AT91_REG PIO_IFER; // Input Filter Enable Register AT91_REG PIO_IFDR; // Input Filter Disable Register AT91_REG PIO_IFSR; // Input Filter Status Register AT91_REG Reserved2[1]; // AT91_REG PIO_SODR; // Set Output Data Register AT91_REG PIO_CODR; // Clear Output Data Register AT91_REG PIO_ODSR; // Output Data Status Register AT91_REG PIO_PDSR; // Pin Data Status Register AT91_REG PIO_IER; // Interrupt Enable Register AT91_REG PIO_IDR; // Interrupt Disable Register AT91_REG PIO_IMR; // Interrupt Mask Register AT91_REG PIO_ISR; // Interrupt Status Register AT91_REG PIO_MDER; // Multi-driver Enable Register AT91_REG PIO_MDDR; // Multi-driver Disable Register AT91_REG PIO_MDSR; // Multi-driver Status Register AT91_REG Reserved3[1]; // AT91_REG PIO_PPUDR; // Pull-up Disable Register AT91_REG PIO_PPUER; // Pull-up Enable Register AT91_REG PIO_PPUSR; // Pull-up Status Register AT91_REG Reserved4[1]; // AT91_REG PIO_ASR; // Select A Register AT91_REG PIO_BSR; // Select B Register AT91_REG PIO_ABSR; // AB Select Status Register AT91_REG Reserved5[9]; // AT91_REG PIO_OWER; // Output Write Enable Register AT91_REG PIO_OWDR; // Output Write Disable Register AT91_REG PIO_OWSR; // Output Write Status Register } AT91S_PIO, *AT91PS_PIO; // ***************************************************************************** // SOFTWARE API DEFINITION FOR Clock Generator Controler // ***************************************************************************** typedef struct _AT91S_CKGR { AT91_REG CKGR_MOR; // Main Oscillator Register AT91_REG CKGR_MCFR; // Main Clock Frequency Register AT91_REG Reserved0[1]; // AT91_REG CKGR_PLLR; // PLL Register } AT91S_CKGR, *AT91PS_CKGR; // -------- CKGR_MOR : (CKGR Offset: 0x0) Main Oscillator Register -------- #define AT91C_CKGR_MOSCEN ((unsigned int) 0x1 << 0) // (CKGR) Main Oscillator Enable #define AT91C_CKGR_OSCBYPASS ((unsigned int) 0x1 << 1) // (CKGR) Main Oscillator Bypass #define AT91C_CKGR_OSCOUNT ((unsigned int) 0xFF << 8) // (CKGR) Main Oscillator Start-up Time // -------- CKGR_MCFR : (CKGR Offset: 0x4) Main Clock Frequency Register -------- #define AT91C_CKGR_MAINF ((unsigned int) 0xFFFF << 0) // (CKGR) Main Clock Frequency #define AT91C_CKGR_MAINRDY ((unsigned int) 0x1 << 16) // (CKGR) Main Clock Ready // -------- CKGR_PLLR : (CKGR Offset: 0xc) PLL B Register -------- #define AT91C_CKGR_DIV ((unsigned int) 0xFF << 0) // (CKGR) Divider Selected #define AT91C_CKGR_DIV_0 ((unsigned int) 0x0) // (CKGR) Divider output is 0 #define AT91C_CKGR_DIV_BYPASS ((unsigned int) 0x1) // (CKGR) Divider is bypassed #define AT91C_CKGR_PLLCOUNT ((unsigned int) 0x3F << 8) // (CKGR) PLL Counter #define AT91C_CKGR_OUT ((unsigned int) 0x3 << 14) // (CKGR) PLL Output Frequency Range #define AT91C_CKGR_OUT_0 ((unsigned int) 0x0 << 14) // (CKGR) Please refer to the PLL datasheet #define AT91C_CKGR_OUT_1 ((unsigned int) 0x1 << 14) // (CKGR) Please refer to the PLL datasheet #define AT91C_CKGR_OUT_2 ((unsigned int) 0x2 << 14) // (CKGR) Please refer to the PLL datasheet #define AT91C_CKGR_OUT_3 ((unsigned int) 0x3 << 14) // (CKGR) Please refer to the PLL datasheet #define AT91C_CKGR_MUL ((unsigned int) 0x7FF << 16) // (CKGR) PLL Multiplier #define AT91C_CKGR_USBDIV ((unsigned int) 0x3 << 28) // (CKGR) Divider for USB Clocks #define AT91C_CKGR_USBDIV_0 ((unsigned int) 0x0 << 28) // (CKGR) Divider output is PLL clock output #define AT91C_CKGR_USBDIV_1 ((unsigned int) 0x1 << 28) // (CKGR) Divider output is PLL clock output divided by 2 #define AT91C_CKGR_USBDIV_2 ((unsigned int) 0x2 << 28) // (CKGR) Divider output is PLL clock output divided by 4 // ***************************************************************************** // SOFTWARE API DEFINITION FOR Power Management Controler // ***************************************************************************** typedef struct _AT91S_PMC { AT91_REG PMC_SCER; // System Clock Enable Register AT91_REG PMC_SCDR; // System Clock Disable Register AT91_REG PMC_SCSR; // System Clock Status Register AT91_REG Reserved0[1]; // AT91_REG PMC_PCER; // Peripheral Clock Enable Register AT91_REG PMC_PCDR; // Peripheral Clock Disable Register AT91_REG PMC_PCSR; // Peripheral Clock Status Register AT91_REG Reserved1[1]; // AT91_REG PMC_MOR; // Main Oscillator Register AT91_REG PMC_MCFR; // Main Clock Frequency Register AT91_REG Reserved2[1]; // AT91_REG PMC_PLLR; // PLL Register AT91_REG PMC_MCKR; // Master Clock Register AT91_REG Reserved3[3]; // AT91_REG PMC_PCKR[3]; // Programmable Clock Register AT91_REG Reserved4[5]; // AT91_REG PMC_IER; // Interrupt Enable Register AT91_REG PMC_IDR; // Interrupt Disable Register AT91_REG PMC_SR; // Status Register AT91_REG PMC_IMR; // Interrupt Mask Register } AT91S_PMC, *AT91PS_PMC; // -------- PMC_SCER : (PMC Offset: 0x0) System Clock Enable Register -------- #define AT91C_PMC_PCK ((unsigned int) 0x1 << 0) // (PMC) Processor Clock #define AT91C_PMC_UDP ((unsigned int) 0x1 << 7) // (PMC) USB Device Port Clock #define AT91C_PMC_PCK0 ((unsigned int) 0x1 << 8) // (PMC) Programmable Clock Output #define AT91C_PMC_PCK1 ((unsigned int) 0x1 << 9) // (PMC) Programmable Clock Output #define AT91C_PMC_PCK2 ((unsigned int) 0x1 << 10) // (PMC) Programmable Clock Output // -------- PMC_SCDR : (PMC Offset: 0x4) System Clock Disable Register -------- // -------- PMC_SCSR : (PMC Offset: 0x8) System Clock Status Register -------- // -------- CKGR_MOR : (PMC Offset: 0x20) Main Oscillator Register -------- // -------- CKGR_MCFR : (PMC Offset: 0x24) Main Clock Frequency Register -------- // -------- CKGR_PLLR : (PMC Offset: 0x2c) PLL B Register -------- // -------- PMC_MCKR : (PMC Offset: 0x30) Master Clock Register -------- #define AT91C_PMC_CSS ((unsigned int) 0x3 << 0) // (PMC) Programmable Clock Selection #define AT91C_PMC_CSS_SLOW_CLK ((unsigned int) 0x0) // (PMC) Slow Clock is selected #define AT91C_PMC_CSS_MAIN_CLK ((unsigned int) 0x1) // (PMC) Main Clock is selected #define AT91C_PMC_CSS_PLL_CLK ((unsigned int) 0x3) // (PMC) Clock from PLL is selected #define AT91C_PMC_PRES ((unsigned int) 0x7 << 2) // (PMC) Programmable Clock Prescaler #define AT91C_PMC_PRES_CLK ((unsigned int) 0x0 << 2) // (PMC) Selected clock #define AT91C_PMC_PRES_CLK_2 ((unsigned int) 0x1 << 2) // (PMC) Selected clock divided by 2 #define AT91C_PMC_PRES_CLK_4 ((unsigned int) 0x2 << 2) // (PMC) Selected clock divided by 4 #define AT91C_PMC_PRES_CLK_8 ((unsigned int) 0x3 << 2) // (PMC) Selected clock divided by 8 #define AT91C_PMC_PRES_CLK_16 ((unsigned int) 0x4 << 2) // (PMC) Selected clock divided by 16 #define AT91C_PMC_PRES_CLK_32 ((unsigned int) 0x5 << 2) // (PMC) Selected clock divided by 32 #define AT91C_PMC_PRES_CLK_64 ((unsigned int) 0x6 << 2) // (PMC) Selected clock divided by 64 // -------- PMC_PCKR : (PMC Offset: 0x40) Programmable Clock Register -------- // -------- PMC_IER : (PMC Offset: 0x60) PMC Interrupt Enable Register -------- #define AT91C_PMC_MOSCS ((unsigned int) 0x1 << 0) // (PMC) MOSC Status/Enable/Disable/Mask #define AT91C_PMC_LOCK ((unsigned int) 0x1 << 2) // (PMC) PLL Status/Enable/Disable/Mask #define AT91C_PMC_MCKRDY ((unsigned int) 0x1 << 3) // (PMC) MCK_RDY Status/Enable/Disable/Mask #define AT91C_PMC_PCK0RDY ((unsigned int) 0x1 << 8) // (PMC) PCK0_RDY Status/Enable/Disable/Mask #define AT91C_PMC_PCK1RDY ((unsigned int) 0x1 << 9) // (PMC) PCK1_RDY Status/Enable/Disable/Mask #define AT91C_PMC_PCK2RDY ((unsigned int) 0x1 << 10) // (PMC) PCK2_RDY Status/Enable/Disable/Mask // -------- PMC_IDR : (PMC Offset: 0x64) PMC Interrupt Disable Register -------- // -------- PMC_SR : (PMC Offset: 0x68) PMC Status Register -------- // -------- PMC_IMR : (PMC Offset: 0x6c) PMC Interrupt Mask Register -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR Reset Controller Interface // ***************************************************************************** typedef struct _AT91S_RSTC { AT91_REG RSTC_RCR; // Reset Control Register AT91_REG RSTC_RSR; // Reset Status Register AT91_REG RSTC_RMR; // Reset Mode Register } AT91S_RSTC, *AT91PS_RSTC; // -------- RSTC_RCR : (RSTC Offset: 0x0) Reset Control Register -------- #define AT91C_RSTC_PROCRST ((unsigned int) 0x1 << 0) // (RSTC) Processor Reset #define AT91C_RSTC_PERRST ((unsigned int) 0x1 << 2) // (RSTC) Peripheral Reset #define AT91C_RSTC_EXTRST ((unsigned int) 0x1 << 3) // (RSTC) External Reset #define AT91C_RSTC_KEY ((unsigned int) 0xFF << 24) // (RSTC) Password // -------- RSTC_RSR : (RSTC Offset: 0x4) Reset Status Register -------- #define AT91C_RSTC_URSTS ((unsigned int) 0x1 << 0) // (RSTC) User Reset Status #define AT91C_RSTC_BODSTS ((unsigned int) 0x1 << 1) // (RSTC) Brownout Detection Status #define AT91C_RSTC_RSTTYP ((unsigned int) 0x7 << 8) // (RSTC) Reset Type #define AT91C_RSTC_RSTTYP_POWERUP ((unsigned int) 0x0 << 8) // (RSTC) Power-up Reset. VDDCORE rising. #define AT91C_RSTC_RSTTYP_WAKEUP ((unsigned int) 0x1 << 8) // (RSTC) WakeUp Reset. VDDCORE rising. #define AT91C_RSTC_RSTTYP_WATCHDOG ((unsigned int) 0x2 << 8) // (RSTC) Watchdog Reset. Watchdog overflow occured. #define AT91C_RSTC_RSTTYP_SOFTWARE ((unsigned int) 0x3 << 8) // (RSTC) Software Reset. Processor reset required by the software. #define AT91C_RSTC_RSTTYP_USER ((unsigned int) 0x4 << 8) // (RSTC) User Reset. NRST pin detected low. #define AT91C_RSTC_RSTTYP_BROWNOUT ((unsigned int) 0x5 << 8) // (RSTC) Brownout Reset occured. #define AT91C_RSTC_NRSTL ((unsigned int) 0x1 << 16) // (RSTC) NRST pin level #define AT91C_RSTC_SRCMP ((unsigned int) 0x1 << 17) // (RSTC) Software Reset Command in Progress. // -------- RSTC_RMR : (RSTC Offset: 0x8) Reset Mode Register -------- #define AT91C_RSTC_URSTEN ((unsigned int) 0x1 << 0) // (RSTC) User Reset Enable #define AT91C_RSTC_URSTIEN ((unsigned int) 0x1 << 4) // (RSTC) User Reset Interrupt Enable #define AT91C_RSTC_ERSTL ((unsigned int) 0xF << 8) // (RSTC) User Reset Enable #define AT91C_RSTC_BODIEN ((unsigned int) 0x1 << 16) // (RSTC) Brownout Detection Interrupt Enable // ***************************************************************************** // SOFTWARE API DEFINITION FOR Real Time Timer Controller Interface // ***************************************************************************** typedef struct _AT91S_RTTC { AT91_REG RTTC_RTMR; // Real-time Mode Register AT91_REG RTTC_RTAR; // Real-time Alarm Register AT91_REG RTTC_RTVR; // Real-time Value Register AT91_REG RTTC_RTSR; // Real-time Status Register } AT91S_RTTC, *AT91PS_RTTC; // -------- RTTC_RTMR : (RTTC Offset: 0x0) Real-time Mode Register -------- #define AT91C_RTTC_RTPRES ((unsigned int) 0xFFFF << 0) // (RTTC) Real-time Timer Prescaler Value #define AT91C_RTTC_ALMIEN ((unsigned int) 0x1 << 16) // (RTTC) Alarm Interrupt Enable #define AT91C_RTTC_RTTINCIEN ((unsigned int) 0x1 << 17) // (RTTC) Real Time Timer Increment Interrupt Enable #define AT91C_RTTC_RTTRST ((unsigned int) 0x1 << 18) // (RTTC) Real Time Timer Restart // -------- RTTC_RTAR : (RTTC Offset: 0x4) Real-time Alarm Register -------- #define AT91C_RTTC_ALMV ((unsigned int) 0x0 << 0) // (RTTC) Alarm Value // -------- RTTC_RTVR : (RTTC Offset: 0x8) Current Real-time Value Register -------- #define AT91C_RTTC_CRTV ((unsigned int) 0x0 << 0) // (RTTC) Current Real-time Value // -------- RTTC_RTSR : (RTTC Offset: 0xc) Real-time Status Register -------- #define AT91C_RTTC_ALMS ((unsigned int) 0x1 << 0) // (RTTC) Real-time Alarm Status #define AT91C_RTTC_RTTINC ((unsigned int) 0x1 << 1) // (RTTC) Real-time Timer Increment // ***************************************************************************** // SOFTWARE API DEFINITION FOR Periodic Interval Timer Controller Interface // ***************************************************************************** typedef struct _AT91S_PITC { AT91_REG PITC_PIMR; // Period Interval Mode Register AT91_REG PITC_PISR; // Period Interval Status Register AT91_REG PITC_PIVR; // Period Interval Value Register AT91_REG PITC_PIIR; // Period Interval Image Register } AT91S_PITC, *AT91PS_PITC; // -------- PITC_PIMR : (PITC Offset: 0x0) Periodic Interval Mode Register -------- #define AT91C_PITC_PIV ((unsigned int) 0xFFFFF << 0) // (PITC) Periodic Interval Value #define AT91C_PITC_PITEN ((unsigned int) 0x1 << 24) // (PITC) Periodic Interval Timer Enabled #define AT91C_PITC_PITIEN ((unsigned int) 0x1 << 25) // (PITC) Periodic Interval Timer Interrupt Enable // -------- PITC_PISR : (PITC Offset: 0x4) Periodic Interval Status Register -------- #define AT91C_PITC_PITS ((unsigned int) 0x1 << 0) // (PITC) Periodic Interval Timer Status // -------- PITC_PIVR : (PITC Offset: 0x8) Periodic Interval Value Register -------- #define AT91C_PITC_CPIV ((unsigned int) 0xFFFFF << 0) // (PITC) Current Periodic Interval Value #define AT91C_PITC_PICNT ((unsigned int) 0xFFF << 20) // (PITC) Periodic Interval Counter // -------- PITC_PIIR : (PITC Offset: 0xc) Periodic Interval Image Register -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR Watchdog Timer Controller Interface // ***************************************************************************** typedef struct _AT91S_WDTC { AT91_REG WDTC_WDCR; // Watchdog Control Register AT91_REG WDTC_WDMR; // Watchdog Mode Register AT91_REG WDTC_WDSR; // Watchdog Status Register } AT91S_WDTC, *AT91PS_WDTC; // -------- WDTC_WDCR : (WDTC Offset: 0x0) Periodic Interval Image Register -------- #define AT91C_WDTC_WDRSTT ((unsigned int) 0x1 << 0) // (WDTC) Watchdog Restart #define AT91C_WDTC_KEY ((unsigned int) 0xFF << 24) // (WDTC) Watchdog KEY Password // -------- WDTC_WDMR : (WDTC Offset: 0x4) Watchdog Mode Register -------- #define AT91C_WDTC_WDV ((unsigned int) 0xFFF << 0) // (WDTC) Watchdog Timer Restart #define AT91C_WDTC_WDFIEN ((unsigned int) 0x1 << 12) // (WDTC) Watchdog Fault Interrupt Enable #define AT91C_WDTC_WDRSTEN ((unsigned int) 0x1 << 13) // (WDTC) Watchdog Reset Enable #define AT91C_WDTC_WDRPROC ((unsigned int) 0x1 << 14) // (WDTC) Watchdog Timer Restart #define AT91C_WDTC_WDDIS ((unsigned int) 0x1 << 15) // (WDTC) Watchdog Disable #define AT91C_WDTC_WDD ((unsigned int) 0xFFF << 16) // (WDTC) Watchdog Delta Value #define AT91C_WDTC_WDDBGHLT ((unsigned int) 0x1 << 28) // (WDTC) Watchdog Debug Halt #define AT91C_WDTC_WDIDLEHLT ((unsigned int) 0x1 << 29) // (WDTC) Watchdog Idle Halt // -------- WDTC_WDSR : (WDTC Offset: 0x8) Watchdog Status Register -------- #define AT91C_WDTC_WDUNF ((unsigned int) 0x1 << 0) // (WDTC) Watchdog Underflow #define AT91C_WDTC_WDERR ((unsigned int) 0x1 << 1) // (WDTC) Watchdog Error // ***************************************************************************** // SOFTWARE API DEFINITION FOR Voltage Regulator Mode Controller Interface // ***************************************************************************** typedef struct _AT91S_VREG { AT91_REG VREG_MR; // Voltage Regulator Mode Register } AT91S_VREG, *AT91PS_VREG; // -------- VREG_MR : (VREG Offset: 0x0) Voltage Regulator Mode Register -------- #define AT91C_VREG_PSTDBY ((unsigned int) 0x1 << 0) // (VREG) Voltage Regulator Power Standby Mode // ***************************************************************************** // SOFTWARE API DEFINITION FOR Memory Controller Interface // ***************************************************************************** typedef struct _AT91S_MC { AT91_REG MC_RCR; // MC Remap Control Register AT91_REG MC_ASR; // MC Abort Status Register AT91_REG MC_AASR; // MC Abort Address Status Register AT91_REG Reserved0[21]; // AT91_REG MC_FMR; // MC Flash Mode Register AT91_REG MC_FCR; // MC Flash Command Register AT91_REG MC_FSR; // MC Flash Status Register } AT91S_MC, *AT91PS_MC; // -------- MC_RCR : (MC Offset: 0x0) MC Remap Control Register -------- #define AT91C_MC_RCB ((unsigned int) 0x1 << 0) // (MC) Remap Command Bit // -------- MC_ASR : (MC Offset: 0x4) MC Abort Status Register -------- #define AT91C_MC_UNDADD ((unsigned int) 0x1 << 0) // (MC) Undefined Addess Abort Status #define AT91C_MC_MISADD ((unsigned int) 0x1 << 1) // (MC) Misaligned Addess Abort Status #define AT91C_MC_ABTSZ ((unsigned int) 0x3 << 8) // (MC) Abort Size Status #define AT91C_MC_ABTSZ_BYTE ((unsigned int) 0x0 << 8) // (MC) Byte #define AT91C_MC_ABTSZ_HWORD ((unsigned int) 0x1 << 8) // (MC) Half-word #define AT91C_MC_ABTSZ_WORD ((unsigned int) 0x2 << 8) // (MC) Word #define AT91C_MC_ABTTYP ((unsigned int) 0x3 << 10) // (MC) Abort Type Status #define AT91C_MC_ABTTYP_DATAR ((unsigned int) 0x0 << 10) // (MC) Data Read #define AT91C_MC_ABTTYP_DATAW ((unsigned int) 0x1 << 10) // (MC) Data Write #define AT91C_MC_ABTTYP_FETCH ((unsigned int) 0x2 << 10) // (MC) Code Fetch #define AT91C_MC_MST0 ((unsigned int) 0x1 << 16) // (MC) Master 0 Abort Source #define AT91C_MC_MST1 ((unsigned int) 0x1 << 17) // (MC) Master 1 Abort Source #define AT91C_MC_SVMST0 ((unsigned int) 0x1 << 24) // (MC) Saved Master 0 Abort Source #define AT91C_MC_SVMST1 ((unsigned int) 0x1 << 25) // (MC) Saved Master 1 Abort Source // -------- MC_FMR : (MC Offset: 0x60) MC Flash Mode Register -------- #define AT91C_MC_FRDY ((unsigned int) 0x1 << 0) // (MC) Flash Ready #define AT91C_MC_LOCKE ((unsigned int) 0x1 << 2) // (MC) Lock Error #define AT91C_MC_PROGE ((unsigned int) 0x1 << 3) // (MC) Programming Error #define AT91C_MC_NEBP ((unsigned int) 0x1 << 7) // (MC) No Erase Before Programming #define AT91C_MC_FWS ((unsigned int) 0x3 << 8) // (MC) Flash Wait State #define AT91C_MC_FWS_0FWS ((unsigned int) 0x0 << 8) // (MC) 1 cycle for Read, 2 for Write operations #define AT91C_MC_FWS_1FWS ((unsigned int) 0x1 << 8) // (MC) 2 cycles for Read, 3 for Write operations #define AT91C_MC_FWS_2FWS ((unsigned int) 0x2 << 8) // (MC) 3 cycles for Read, 4 for Write operations #define AT91C_MC_FWS_3FWS ((unsigned int) 0x3 << 8) // (MC) 4 cycles for Read, 4 for Write operations #define AT91C_MC_FMCN ((unsigned int) 0xFF << 16) // (MC) Flash Microsecond Cycle Number // -------- MC_FCR : (MC Offset: 0x64) MC Flash Command Register -------- #define AT91C_MC_FCMD ((unsigned int) 0xF << 0) // (MC) Flash Command #define AT91C_MC_FCMD_START_PROG ((unsigned int) 0x1) // (MC) Starts the programming of th epage specified by PAGEN. #define AT91C_MC_FCMD_LOCK ((unsigned int) 0x2) // (MC) Starts a lock sequence of the sector defined by the bits 4 to 7 of the field PAGEN. #define AT91C_MC_FCMD_PROG_AND_LOCK ((unsigned int) 0x3) // (MC) The lock sequence automatically happens after the programming sequence is completed. #define AT91C_MC_FCMD_UNLOCK ((unsigned int) 0x4) // (MC) Starts an unlock sequence of the sector defined by the bits 4 to 7 of the field PAGEN. #define AT91C_MC_FCMD_ERASE_ALL ((unsigned int) 0x8) // (MC) Starts the erase of the entire flash.If at least a page is locked, the command is cancelled. #define AT91C_MC_FCMD_SET_GP_NVM ((unsigned int) 0xB) // (MC) Set General Purpose NVM bits. #define AT91C_MC_FCMD_CLR_GP_NVM ((unsigned int) 0xD) // (MC) Clear General Purpose NVM bits. #define AT91C_MC_FCMD_SET_SECURITY ((unsigned int) 0xF) // (MC) Set Security Bit. #define AT91C_MC_PAGEN ((unsigned int) 0x3FF << 8) // (MC) Page Number #define AT91C_MC_KEY ((unsigned int) 0xFF << 24) // (MC) Writing Protect Key // -------- MC_FSR : (MC Offset: 0x68) MC Flash Command Register -------- #define AT91C_MC_SECURITY ((unsigned int) 0x1 << 4) // (MC) Security Bit Status #define AT91C_MC_GPNVM0 ((unsigned int) 0x1 << 8) // (MC) Sector 0 Lock Status #define AT91C_MC_GPNVM1 ((unsigned int) 0x1 << 9) // (MC) Sector 1 Lock Status #define AT91C_MC_GPNVM2 ((unsigned int) 0x1 << 10) // (MC) Sector 2 Lock Status #define AT91C_MC_GPNVM3 ((unsigned int) 0x1 << 11) // (MC) Sector 3 Lock Status #define AT91C_MC_GPNVM4 ((unsigned int) 0x1 << 12) // (MC) Sector 4 Lock Status #define AT91C_MC_GPNVM5 ((unsigned int) 0x1 << 13) // (MC) Sector 5 Lock Status #define AT91C_MC_GPNVM6 ((unsigned int) 0x1 << 14) // (MC) Sector 6 Lock Status #define AT91C_MC_GPNVM7 ((unsigned int) 0x1 << 15) // (MC) Sector 7 Lock Status #define AT91C_MC_LOCKS0 ((unsigned int) 0x1 << 16) // (MC) Sector 0 Lock Status #define AT91C_MC_LOCKS1 ((unsigned int) 0x1 << 17) // (MC) Sector 1 Lock Status #define AT91C_MC_LOCKS2 ((unsigned int) 0x1 << 18) // (MC) Sector 2 Lock Status #define AT91C_MC_LOCKS3 ((unsigned int) 0x1 << 19) // (MC) Sector 3 Lock Status #define AT91C_MC_LOCKS4 ((unsigned int) 0x1 << 20) // (MC) Sector 4 Lock Status #define AT91C_MC_LOCKS5 ((unsigned int) 0x1 << 21) // (MC) Sector 5 Lock Status #define AT91C_MC_LOCKS6 ((unsigned int) 0x1 << 22) // (MC) Sector 6 Lock Status #define AT91C_MC_LOCKS7 ((unsigned int) 0x1 << 23) // (MC) Sector 7 Lock Status #define AT91C_MC_LOCKS8 ((unsigned int) 0x1 << 24) // (MC) Sector 8 Lock Status #define AT91C_MC_LOCKS9 ((unsigned int) 0x1 << 25) // (MC) Sector 9 Lock Status #define AT91C_MC_LOCKS10 ((unsigned int) 0x1 << 26) // (MC) Sector 10 Lock Status #define AT91C_MC_LOCKS11 ((unsigned int) 0x1 << 27) // (MC) Sector 11 Lock Status #define AT91C_MC_LOCKS12 ((unsigned int) 0x1 << 28) // (MC) Sector 12 Lock Status #define AT91C_MC_LOCKS13 ((unsigned int) 0x1 << 29) // (MC) Sector 13 Lock Status #define AT91C_MC_LOCKS14 ((unsigned int) 0x1 << 30) // (MC) Sector 14 Lock Status #define AT91C_MC_LOCKS15 ((unsigned int) 0x1 << 31) // (MC) Sector 15 Lock Status // ***************************************************************************** // SOFTWARE API DEFINITION FOR Serial Parallel Interface // ***************************************************************************** typedef struct _AT91S_SPI { AT91_REG SPI_CR; // Control Register AT91_REG SPI_MR; // Mode Register AT91_REG SPI_RDR; // Receive Data Register AT91_REG SPI_TDR; // Transmit Data Register AT91_REG SPI_SR; // Status Register AT91_REG SPI_IER; // Interrupt Enable Register AT91_REG SPI_IDR; // Interrupt Disable Register AT91_REG SPI_IMR; // Interrupt Mask Register AT91_REG Reserved0[4]; // AT91_REG SPI_CSR[4]; // Chip Select Register AT91_REG Reserved1[48]; // AT91_REG SPI_RPR; // Receive Pointer Register AT91_REG SPI_RCR; // Receive Counter Register AT91_REG SPI_TPR; // Transmit Pointer Register AT91_REG SPI_TCR; // Transmit Counter Register AT91_REG SPI_RNPR; // Receive Next Pointer Register AT91_REG SPI_RNCR; // Receive Next Counter Register AT91_REG SPI_TNPR; // Transmit Next Pointer Register AT91_REG SPI_TNCR; // Transmit Next Counter Register AT91_REG SPI_PTCR; // PDC Transfer Control Register AT91_REG SPI_PTSR; // PDC Transfer Status Register } AT91S_SPI, *AT91PS_SPI; // -------- SPI_CR : (SPI Offset: 0x0) SPI Control Register -------- #define AT91C_SPI_SPIEN ((unsigned int) 0x1 << 0) // (SPI) SPI Enable #define AT91C_SPI_SPIDIS ((unsigned int) 0x1 << 1) // (SPI) SPI Disable #define AT91C_SPI_SWRST ((unsigned int) 0x1 << 7) // (SPI) SPI Software reset #define AT91C_SPI_LASTXFER ((unsigned int) 0x1 << 24) // (SPI) SPI Last Transfer // -------- SPI_MR : (SPI Offset: 0x4) SPI Mode Register -------- #define AT91C_SPI_MSTR ((unsigned int) 0x1 << 0) // (SPI) Master/Slave Mode #define AT91C_SPI_PS ((unsigned int) 0x1 << 1) // (SPI) Peripheral Select #define AT91C_SPI_PS_FIXED ((unsigned int) 0x0 << 1) // (SPI) Fixed Peripheral Select #define AT91C_SPI_PS_VARIABLE ((unsigned int) 0x1 << 1) // (SPI) Variable Peripheral Select #define AT91C_SPI_PCSDEC ((unsigned int) 0x1 << 2) // (SPI) Chip Select Decode #define AT91C_SPI_FDIV ((unsigned int) 0x1 << 3) // (SPI) Clock Selection #define AT91C_SPI_MODFDIS ((unsigned int) 0x1 << 4) // (SPI) Mode Fault Detection #define AT91C_SPI_LLB ((unsigned int) 0x1 << 7) // (SPI) Clock Selection #define AT91C_SPI_PCS ((unsigned int) 0xF << 16) // (SPI) Peripheral Chip Select #define AT91C_SPI_DLYBCS ((unsigned int) 0xFF << 24) // (SPI) Delay Between Chip Selects // -------- SPI_RDR : (SPI Offset: 0x8) Receive Data Register -------- #define AT91C_SPI_RD ((unsigned int) 0xFFFF << 0) // (SPI) Receive Data #define AT91C_SPI_RPCS ((unsigned int) 0xF << 16) // (SPI) Peripheral Chip Select Status // -------- SPI_TDR : (SPI Offset: 0xc) Transmit Data Register -------- #define AT91C_SPI_TD ((unsigned int) 0xFFFF << 0) // (SPI) Transmit Data #define AT91C_SPI_TPCS ((unsigned int) 0xF << 16) // (SPI) Peripheral Chip Select Status // -------- SPI_SR : (SPI Offset: 0x10) Status Register -------- #define AT91C_SPI_RDRF ((unsigned int) 0x1 << 0) // (SPI) Receive Data Register Full #define AT91C_SPI_TDRE ((unsigned int) 0x1 << 1) // (SPI) Transmit Data Register Empty #define AT91C_SPI_MODF ((unsigned int) 0x1 << 2) // (SPI) Mode Fault Error #define AT91C_SPI_OVRES ((unsigned int) 0x1 << 3) // (SPI) Overrun Error Status #define AT91C_SPI_ENDRX ((unsigned int) 0x1 << 4) // (SPI) End of Receiver Transfer #define AT91C_SPI_ENDTX ((unsigned int) 0x1 << 5) // (SPI) End of Receiver Transfer #define AT91C_SPI_RXBUFF ((unsigned int) 0x1 << 6) // (SPI) RXBUFF Interrupt #define AT91C_SPI_TXBUFE ((unsigned int) 0x1 << 7) // (SPI) TXBUFE Interrupt #define AT91C_SPI_NSSR ((unsigned int) 0x1 << 8) // (SPI) NSSR Interrupt #define AT91C_SPI_TXEMPTY ((unsigned int) 0x1 << 9) // (SPI) TXEMPTY Interrupt #define AT91C_SPI_SPIENS ((unsigned int) 0x1 << 16) // (SPI) Enable Status // -------- SPI_IER : (SPI Offset: 0x14) Interrupt Enable Register -------- // -------- SPI_IDR : (SPI Offset: 0x18) Interrupt Disable Register -------- // -------- SPI_IMR : (SPI Offset: 0x1c) Interrupt Mask Register -------- // -------- SPI_CSR : (SPI Offset: 0x30) Chip Select Register -------- #define AT91C_SPI_CPOL ((unsigned int) 0x1 << 0) // (SPI) Clock Polarity #define AT91C_SPI_NCPHA ((unsigned int) 0x1 << 1) // (SPI) Clock Phase #define AT91C_SPI_CSAAT ((unsigned int) 0x1 << 3) // (SPI) Chip Select Active After Transfer #define AT91C_SPI_BITS ((unsigned int) 0xF << 4) // (SPI) Bits Per Transfer #define AT91C_SPI_BITS_8 ((unsigned int) 0x0 << 4) // (SPI) 8 Bits Per transfer #define AT91C_SPI_BITS_9 ((unsigned int) 0x1 << 4) // (SPI) 9 Bits Per transfer #define AT91C_SPI_BITS_10 ((unsigned int) 0x2 << 4) // (SPI) 10 Bits Per transfer #define AT91C_SPI_BITS_11 ((unsigned int) 0x3 << 4) // (SPI) 11 Bits Per transfer #define AT91C_SPI_BITS_12 ((unsigned int) 0x4 << 4) // (SPI) 12 Bits Per transfer #define AT91C_SPI_BITS_13 ((unsigned int) 0x5 << 4) // (SPI) 13 Bits Per transfer #define AT91C_SPI_BITS_14 ((unsigned int) 0x6 << 4) // (SPI) 14 Bits Per transfer #define AT91C_SPI_BITS_15 ((unsigned int) 0x7 << 4) // (SPI) 15 Bits Per transfer #define AT91C_SPI_BITS_16 ((unsigned int) 0x8 << 4) // (SPI) 16 Bits Per transfer #define AT91C_SPI_SCBR ((unsigned int) 0xFF << 8) // (SPI) Serial Clock Baud Rate #define AT91C_SPI_DLYBS ((unsigned int) 0xFF << 16) // (SPI) Serial Clock Baud Rate #define AT91C_SPI_DLYBCT ((unsigned int) 0xFF << 24) // (SPI) Delay Between Consecutive Transfers // ***************************************************************************** // SOFTWARE API DEFINITION FOR Analog to Digital Convertor // ***************************************************************************** typedef struct _AT91S_ADC { AT91_REG ADC_CR; // ADC Control Register AT91_REG ADC_MR; // ADC Mode Register AT91_REG Reserved0[2]; // AT91_REG ADC_CHER; // ADC Channel Enable Register AT91_REG ADC_CHDR; // ADC Channel Disable Register AT91_REG ADC_CHSR; // ADC Channel Status Register AT91_REG ADC_SR; // ADC Status Register AT91_REG ADC_LCDR; // ADC Last Converted Data Register AT91_REG ADC_IER; // ADC Interrupt Enable Register AT91_REG ADC_IDR; // ADC Interrupt Disable Register AT91_REG ADC_IMR; // ADC Interrupt Mask Register AT91_REG ADC_CDR0; // ADC Channel Data Register 0 AT91_REG ADC_CDR1; // ADC Channel Data Register 1 AT91_REG ADC_CDR2; // ADC Channel Data Register 2 AT91_REG ADC_CDR3; // ADC Channel Data Register 3 AT91_REG ADC_CDR4; // ADC Channel Data Register 4 AT91_REG ADC_CDR5; // ADC Channel Data Register 5 AT91_REG ADC_CDR6; // ADC Channel Data Register 6 AT91_REG ADC_CDR7; // ADC Channel Data Register 7 AT91_REG Reserved1[44]; // AT91_REG ADC_RPR; // Receive Pointer Register AT91_REG ADC_RCR; // Receive Counter Register AT91_REG ADC_TPR; // Transmit Pointer Register AT91_REG ADC_TCR; // Transmit Counter Register AT91_REG ADC_RNPR; // Receive Next Pointer Register AT91_REG ADC_RNCR; // Receive Next Counter Register AT91_REG ADC_TNPR; // Transmit Next Pointer Register AT91_REG ADC_TNCR; // Transmit Next Counter Register AT91_REG ADC_PTCR; // PDC Transfer Control Register AT91_REG ADC_PTSR; // PDC Transfer Status Register } AT91S_ADC, *AT91PS_ADC; // -------- ADC_CR : (ADC Offset: 0x0) ADC Control Register -------- #define AT91C_ADC_SWRST ((unsigned int) 0x1 << 0) // (ADC) Software Reset #define AT91C_ADC_START ((unsigned int) 0x1 << 1) // (ADC) Start Conversion // -------- ADC_MR : (ADC Offset: 0x4) ADC Mode Register -------- #define AT91C_ADC_TRGEN ((unsigned int) 0x1 << 0) // (ADC) Trigger Enable #define AT91C_ADC_TRGEN_DIS ((unsigned int) 0x0) // (ADC) Hradware triggers are disabled. Starting a conversion is only possible by software #define AT91C_ADC_TRGEN_EN ((unsigned int) 0x1) // (ADC) Hardware trigger selected by TRGSEL field is enabled. #define AT91C_ADC_TRGSEL ((unsigned int) 0x7 << 1) // (ADC) Trigger Selection #define AT91C_ADC_TRGSEL_TIOA0 ((unsigned int) 0x0 << 1) // (ADC) Selected TRGSEL = TIAO0 #define AT91C_ADC_TRGSEL_TIOA1 ((unsigned int) 0x1 << 1) // (ADC) Selected TRGSEL = TIAO1 #define AT91C_ADC_TRGSEL_TIOA2 ((unsigned int) 0x2 << 1) // (ADC) Selected TRGSEL = TIAO2 #define AT91C_ADC_TRGSEL_TIOA3 ((unsigned int) 0x3 << 1) // (ADC) Selected TRGSEL = TIAO3 #define AT91C_ADC_TRGSEL_TIOA4 ((unsigned int) 0x4 << 1) // (ADC) Selected TRGSEL = TIAO4 #define AT91C_ADC_TRGSEL_TIOA5 ((unsigned int) 0x5 << 1) // (ADC) Selected TRGSEL = TIAO5 #define AT91C_ADC_TRGSEL_EXT ((unsigned int) 0x6 << 1) // (ADC) Selected TRGSEL = External Trigger #define AT91C_ADC_LOWRES ((unsigned int) 0x1 << 4) // (ADC) Resolution. #define AT91C_ADC_LOWRES_10_BIT ((unsigned int) 0x0 << 4) // (ADC) 10-bit resolution #define AT91C_ADC_LOWRES_8_BIT ((unsigned int) 0x1 << 4) // (ADC) 8-bit resolution #define AT91C_ADC_SLEEP ((unsigned int) 0x1 << 5) // (ADC) Sleep Mode #define AT91C_ADC_SLEEP_NORMAL_MODE ((unsigned int) 0x0 << 5) // (ADC) Normal Mode #define AT91C_ADC_SLEEP_MODE ((unsigned int) 0x1 << 5) // (ADC) Sleep Mode #define AT91C_ADC_PRESCAL ((unsigned int) 0x3F << 8) // (ADC) Prescaler rate selection #define AT91C_ADC_STARTUP ((unsigned int) 0x1F << 16) // (ADC) Startup Time #define AT91C_ADC_SHTIM ((unsigned int) 0xF << 24) // (ADC) Sample & Hold Time // -------- ADC_CHER : (ADC Offset: 0x10) ADC Channel Enable Register -------- #define AT91C_ADC_CH0 ((unsigned int) 0x1 << 0) // (ADC) Channel 0 #define AT91C_ADC_CH1 ((unsigned int) 0x1 << 1) // (ADC) Channel 1 #define AT91C_ADC_CH2 ((unsigned int) 0x1 << 2) // (ADC) Channel 2 #define AT91C_ADC_CH3 ((unsigned int) 0x1 << 3) // (ADC) Channel 3 #define AT91C_ADC_CH4 ((unsigned int) 0x1 << 4) // (ADC) Channel 4 #define AT91C_ADC_CH5 ((unsigned int) 0x1 << 5) // (ADC) Channel 5 #define AT91C_ADC_CH6 ((unsigned int) 0x1 << 6) // (ADC) Channel 6 #define AT91C_ADC_CH7 ((unsigned int) 0x1 << 7) // (ADC) Channel 7 // -------- ADC_CHDR : (ADC Offset: 0x14) ADC Channel Disable Register -------- // -------- ADC_CHSR : (ADC Offset: 0x18) ADC Channel Status Register -------- // -------- ADC_SR : (ADC Offset: 0x1c) ADC Status Register -------- #define AT91C_ADC_EOC0 ((unsigned int) 0x1 << 0) // (ADC) End of Conversion #define AT91C_ADC_EOC1 ((unsigned int) 0x1 << 1) // (ADC) End of Conversion #define AT91C_ADC_EOC2 ((unsigned int) 0x1 << 2) // (ADC) End of Conversion #define AT91C_ADC_EOC3 ((unsigned int) 0x1 << 3) // (ADC) End of Conversion #define AT91C_ADC_EOC4 ((unsigned int) 0x1 << 4) // (ADC) End of Conversion #define AT91C_ADC_EOC5 ((unsigned int) 0x1 << 5) // (ADC) End of Conversion #define AT91C_ADC_EOC6 ((unsigned int) 0x1 << 6) // (ADC) End of Conversion #define AT91C_ADC_EOC7 ((unsigned int) 0x1 << 7) // (ADC) End of Conversion #define AT91C_ADC_OVRE0 ((unsigned int) 0x1 << 8) // (ADC) Overrun Error #define AT91C_ADC_OVRE1 ((unsigned int) 0x1 << 9) // (ADC) Overrun Error #define AT91C_ADC_OVRE2 ((unsigned int) 0x1 << 10) // (ADC) Overrun Error #define AT91C_ADC_OVRE3 ((unsigned int) 0x1 << 11) // (ADC) Overrun Error #define AT91C_ADC_OVRE4 ((unsigned int) 0x1 << 12) // (ADC) Overrun Error #define AT91C_ADC_OVRE5 ((unsigned int) 0x1 << 13) // (ADC) Overrun Error #define AT91C_ADC_OVRE6 ((unsigned int) 0x1 << 14) // (ADC) Overrun Error #define AT91C_ADC_OVRE7 ((unsigned int) 0x1 << 15) // (ADC) Overrun Error #define AT91C_ADC_DRDY ((unsigned int) 0x1 << 16) // (ADC) Data Ready #define AT91C_ADC_GOVRE ((unsigned int) 0x1 << 17) // (ADC) General Overrun #define AT91C_ADC_ENDRX ((unsigned int) 0x1 << 18) // (ADC) End of Receiver Transfer #define AT91C_ADC_RXBUFF ((unsigned int) 0x1 << 19) // (ADC) RXBUFF Interrupt // -------- ADC_LCDR : (ADC Offset: 0x20) ADC Last Converted Data Register -------- #define AT91C_ADC_LDATA ((unsigned int) 0x3FF << 0) // (ADC) Last Data Converted // -------- ADC_IER : (ADC Offset: 0x24) ADC Interrupt Enable Register -------- // -------- ADC_IDR : (ADC Offset: 0x28) ADC Interrupt Disable Register -------- // -------- ADC_IMR : (ADC Offset: 0x2c) ADC Interrupt Mask Register -------- // -------- ADC_CDR0 : (ADC Offset: 0x30) ADC Channel Data Register 0 -------- #define AT91C_ADC_DATA ((unsigned int) 0x3FF << 0) // (ADC) Converted Data // -------- ADC_CDR1 : (ADC Offset: 0x34) ADC Channel Data Register 1 -------- // -------- ADC_CDR2 : (ADC Offset: 0x38) ADC Channel Data Register 2 -------- // -------- ADC_CDR3 : (ADC Offset: 0x3c) ADC Channel Data Register 3 -------- // -------- ADC_CDR4 : (ADC Offset: 0x40) ADC Channel Data Register 4 -------- // -------- ADC_CDR5 : (ADC Offset: 0x44) ADC Channel Data Register 5 -------- // -------- ADC_CDR6 : (ADC Offset: 0x48) ADC Channel Data Register 6 -------- // -------- ADC_CDR7 : (ADC Offset: 0x4c) ADC Channel Data Register 7 -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR Synchronous Serial Controller Interface // ***************************************************************************** typedef struct _AT91S_SSC { AT91_REG SSC_CR; // Control Register AT91_REG SSC_CMR; // Clock Mode Register AT91_REG Reserved0[2]; // AT91_REG SSC_RCMR; // Receive Clock ModeRegister AT91_REG SSC_RFMR; // Receive Frame Mode Register AT91_REG SSC_TCMR; // Transmit Clock Mode Register AT91_REG SSC_TFMR; // Transmit Frame Mode Register AT91_REG SSC_RHR; // Receive Holding Register AT91_REG SSC_THR; // Transmit Holding Register AT91_REG Reserved1[2]; // AT91_REG SSC_RSHR; // Receive Sync Holding Register AT91_REG SSC_TSHR; // Transmit Sync Holding Register AT91_REG Reserved2[2]; // AT91_REG SSC_SR; // Status Register AT91_REG SSC_IER; // Interrupt Enable Register AT91_REG SSC_IDR; // Interrupt Disable Register AT91_REG SSC_IMR; // Interrupt Mask Register AT91_REG Reserved3[44]; // AT91_REG SSC_RPR; // Receive Pointer Register AT91_REG SSC_RCR; // Receive Counter Register AT91_REG SSC_TPR; // Transmit Pointer Register AT91_REG SSC_TCR; // Transmit Counter Register AT91_REG SSC_RNPR; // Receive Next Pointer Register AT91_REG SSC_RNCR; // Receive Next Counter Register AT91_REG SSC_TNPR; // Transmit Next Pointer Register AT91_REG SSC_TNCR; // Transmit Next Counter Register AT91_REG SSC_PTCR; // PDC Transfer Control Register AT91_REG SSC_PTSR; // PDC Transfer Status Register } AT91S_SSC, *AT91PS_SSC; // -------- SSC_CR : (SSC Offset: 0x0) SSC Control Register -------- #define AT91C_SSC_RXEN ((unsigned int) 0x1 << 0) // (SSC) Receive Enable #define AT91C_SSC_RXDIS ((unsigned int) 0x1 << 1) // (SSC) Receive Disable #define AT91C_SSC_TXEN ((unsigned int) 0x1 << 8) // (SSC) Transmit Enable #define AT91C_SSC_TXDIS ((unsigned int) 0x1 << 9) // (SSC) Transmit Disable #define AT91C_SSC_SWRST ((unsigned int) 0x1 << 15) // (SSC) Software Reset // -------- SSC_RCMR : (SSC Offset: 0x10) SSC Receive Clock Mode Register -------- #define AT91C_SSC_CKS ((unsigned int) 0x3 << 0) // (SSC) Receive/Transmit Clock Selection #define AT91C_SSC_CKS_DIV ((unsigned int) 0x0) // (SSC) Divided Clock #define AT91C_SSC_CKS_TK ((unsigned int) 0x1) // (SSC) TK Clock signal #define AT91C_SSC_CKS_RK ((unsigned int) 0x2) // (SSC) RK pin #define AT91C_SSC_CKO ((unsigned int) 0x7 << 2) // (SSC) Receive/Transmit Clock Output Mode Selection #define AT91C_SSC_CKO_NONE ((unsigned int) 0x0 << 2) // (SSC) Receive/Transmit Clock Output Mode: None RK pin: Input-only #define AT91C_SSC_CKO_CONTINOUS ((unsigned int) 0x1 << 2) // (SSC) Continuous Receive/Transmit Clock RK pin: Output #define AT91C_SSC_CKO_DATA_TX ((unsigned int) 0x2 << 2) // (SSC) Receive/Transmit Clock only during data transfers RK pin: Output #define AT91C_SSC_CKI ((unsigned int) 0x1 << 5) // (SSC) Receive/Transmit Clock Inversion #define AT91C_SSC_START ((unsigned int) 0xF << 8) // (SSC) Receive/Transmit Start Selection #define AT91C_SSC_START_CONTINOUS ((unsigned int) 0x0 << 8) // (SSC) Continuous, as soon as the receiver is enabled, and immediately after the end of transfer of the previous data. #define AT91C_SSC_START_TX ((unsigned int) 0x1 << 8) // (SSC) Transmit/Receive start #define AT91C_SSC_START_LOW_RF ((unsigned int) 0x2 << 8) // (SSC) Detection of a low level on RF input #define AT91C_SSC_START_HIGH_RF ((unsigned int) 0x3 << 8) // (SSC) Detection of a high level on RF input #define AT91C_SSC_START_FALL_RF ((unsigned int) 0x4 << 8) // (SSC) Detection of a falling edge on RF input #define AT91C_SSC_START_RISE_RF ((unsigned int) 0x5 << 8) // (SSC) Detection of a rising edge on RF input #define AT91C_SSC_START_LEVEL_RF ((unsigned int) 0x6 << 8) // (SSC) Detection of any level change on RF input #define AT91C_SSC_START_EDGE_RF ((unsigned int) 0x7 << 8) // (SSC) Detection of any edge on RF input #define AT91C_SSC_START_0 ((unsigned int) 0x8 << 8) // (SSC) Compare 0 #define AT91C_SSC_STTDLY ((unsigned int) 0xFF << 16) // (SSC) Receive/Transmit Start Delay #define AT91C_SSC_PERIOD ((unsigned int) 0xFF << 24) // (SSC) Receive/Transmit Period Divider Selection // -------- SSC_RFMR : (SSC Offset: 0x14) SSC Receive Frame Mode Register -------- #define AT91C_SSC_DATLEN ((unsigned int) 0x1F << 0) // (SSC) Data Length #define AT91C_SSC_LOOP ((unsigned int) 0x1 << 5) // (SSC) Loop Mode #define AT91C_SSC_MSBF ((unsigned int) 0x1 << 7) // (SSC) Most Significant Bit First #define AT91C_SSC_DATNB ((unsigned int) 0xF << 8) // (SSC) Data Number per Frame #define AT91C_SSC_FSLEN ((unsigned int) 0xF << 16) // (SSC) Receive/Transmit Frame Sync length #define AT91C_SSC_FSOS ((unsigned int) 0x7 << 20) // (SSC) Receive/Transmit Frame Sync Output Selection #define AT91C_SSC_FSOS_NONE ((unsigned int) 0x0 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: None RK pin Input-only #define AT91C_SSC_FSOS_NEGATIVE ((unsigned int) 0x1 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Negative Pulse #define AT91C_SSC_FSOS_POSITIVE ((unsigned int) 0x2 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Positive Pulse #define AT91C_SSC_FSOS_LOW ((unsigned int) 0x3 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Driver Low during data transfer #define AT91C_SSC_FSOS_HIGH ((unsigned int) 0x4 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Driver High during data transfer #define AT91C_SSC_FSOS_TOGGLE ((unsigned int) 0x5 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Toggling at each start of data transfer #define AT91C_SSC_FSEDGE ((unsigned int) 0x1 << 24) // (SSC) Frame Sync Edge Detection // -------- SSC_TCMR : (SSC Offset: 0x18) SSC Transmit Clock Mode Register -------- // -------- SSC_TFMR : (SSC Offset: 0x1c) SSC Transmit Frame Mode Register -------- #define AT91C_SSC_DATDEF ((unsigned int) 0x1 << 5) // (SSC) Data Default Value #define AT91C_SSC_FSDEN ((unsigned int) 0x1 << 23) // (SSC) Frame Sync Data Enable // -------- SSC_SR : (SSC Offset: 0x40) SSC Status Register -------- #define AT91C_SSC_TXRDY ((unsigned int) 0x1 << 0) // (SSC) Transmit Ready #define AT91C_SSC_TXEMPTY ((unsigned int) 0x1 << 1) // (SSC) Transmit Empty #define AT91C_SSC_ENDTX ((unsigned int) 0x1 << 2) // (SSC) End Of Transmission #define AT91C_SSC_TXBUFE ((unsigned int) 0x1 << 3) // (SSC) Transmit Buffer Empty #define AT91C_SSC_RXRDY ((unsigned int) 0x1 << 4) // (SSC) Receive Ready #define AT91C_SSC_OVRUN ((unsigned int) 0x1 << 5) // (SSC) Receive Overrun #define AT91C_SSC_ENDRX ((unsigned int) 0x1 << 6) // (SSC) End of Reception #define AT91C_SSC_RXBUFF ((unsigned int) 0x1 << 7) // (SSC) Receive Buffer Full #define AT91C_SSC_TXSYN ((unsigned int) 0x1 << 10) // (SSC) Transmit Sync #define AT91C_SSC_RXSYN ((unsigned int) 0x1 << 11) // (SSC) Receive Sync #define AT91C_SSC_TXENA ((unsigned int) 0x1 << 16) // (SSC) Transmit Enable #define AT91C_SSC_RXENA ((unsigned int) 0x1 << 17) // (SSC) Receive Enable // -------- SSC_IER : (SSC Offset: 0x44) SSC Interrupt Enable Register -------- // -------- SSC_IDR : (SSC Offset: 0x48) SSC Interrupt Disable Register -------- // -------- SSC_IMR : (SSC Offset: 0x4c) SSC Interrupt Mask Register -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR Usart // ***************************************************************************** typedef struct _AT91S_USART { AT91_REG US_CR; // Control Register AT91_REG US_MR; // Mode Register AT91_REG US_IER; // Interrupt Enable Register AT91_REG US_IDR; // Interrupt Disable Register AT91_REG US_IMR; // Interrupt Mask Register AT91_REG US_CSR; // Channel Status Register AT91_REG US_RHR; // Receiver Holding Register AT91_REG US_THR; // Transmitter Holding Register AT91_REG US_BRGR; // Baud Rate Generator Register AT91_REG US_RTOR; // Receiver Time-out Register AT91_REG US_TTGR; // Transmitter Time-guard Register AT91_REG Reserved0[5]; // AT91_REG US_FIDI; // FI_DI_Ratio Register AT91_REG US_NER; // Nb Errors Register AT91_REG Reserved1[1]; // AT91_REG US_IF; // IRDA_FILTER Register AT91_REG Reserved2[44]; // AT91_REG US_RPR; // Receive Pointer Register AT91_REG US_RCR; // Receive Counter Register AT91_REG US_TPR; // Transmit Pointer Register AT91_REG US_TCR; // Transmit Counter Register AT91_REG US_RNPR; // Receive Next Pointer Register AT91_REG US_RNCR; // Receive Next Counter Register AT91_REG US_TNPR; // Transmit Next Pointer Register AT91_REG US_TNCR; // Transmit Next Counter Register AT91_REG US_PTCR; // PDC Transfer Control Register AT91_REG US_PTSR; // PDC Transfer Status Register } AT91S_USART, *AT91PS_USART; // -------- US_CR : (USART Offset: 0x0) Debug Unit Control Register -------- #define AT91C_US_STTBRK ((unsigned int) 0x1 << 9) // (USART) Start Break #define AT91C_US_STPBRK ((unsigned int) 0x1 << 10) // (USART) Stop Break #define AT91C_US_STTTO ((unsigned int) 0x1 << 11) // (USART) Start Time-out #define AT91C_US_SENDA ((unsigned int) 0x1 << 12) // (USART) Send Address #define AT91C_US_RSTIT ((unsigned int) 0x1 << 13) // (USART) Reset Iterations #define AT91C_US_RSTNACK ((unsigned int) 0x1 << 14) // (USART) Reset Non Acknowledge #define AT91C_US_RETTO ((unsigned int) 0x1 << 15) // (USART) Rearm Time-out #define AT91C_US_DTREN ((unsigned int) 0x1 << 16) // (USART) Data Terminal ready Enable #define AT91C_US_DTRDIS ((unsigned int) 0x1 << 17) // (USART) Data Terminal ready Disable #define AT91C_US_RTSEN ((unsigned int) 0x1 << 18) // (USART) Request to Send enable #define AT91C_US_RTSDIS ((unsigned int) 0x1 << 19) // (USART) Request to Send Disable // -------- US_MR : (USART Offset: 0x4) Debug Unit Mode Register -------- #define AT91C_US_USMODE ((unsigned int) 0xF << 0) // (USART) Usart mode #define AT91C_US_USMODE_NORMAL ((unsigned int) 0x0) // (USART) Normal #define AT91C_US_USMODE_RS485 ((unsigned int) 0x1) // (USART) RS485 #define AT91C_US_USMODE_HWHSH ((unsigned int) 0x2) // (USART) Hardware Handshaking #define AT91C_US_USMODE_MODEM ((unsigned int) 0x3) // (USART) Modem #define AT91C_US_USMODE_ISO7816_0 ((unsigned int) 0x4) // (USART) ISO7816 protocol: T = 0 #define AT91C_US_USMODE_ISO7816_1 ((unsigned int) 0x6) // (USART) ISO7816 protocol: T = 1 #define AT91C_US_USMODE_IRDA ((unsigned int) 0x8) // (USART) IrDA #define AT91C_US_USMODE_SWHSH ((unsigned int) 0xC) // (USART) Software Handshaking #define AT91C_US_CLKS ((unsigned int) 0x3 << 4) // (USART) Clock Selection (Baud Rate generator Input Clock #define AT91C_US_CLKS_CLOCK ((unsigned int) 0x0 << 4) // (USART) Clock #define AT91C_US_CLKS_FDIV1 ((unsigned int) 0x1 << 4) // (USART) fdiv1 #define AT91C_US_CLKS_SLOW ((unsigned int) 0x2 << 4) // (USART) slow_clock (ARM) #define AT91C_US_CLKS_EXT ((unsigned int) 0x3 << 4) // (USART) External (SCK) #define AT91C_US_CHRL ((unsigned int) 0x3 << 6) // (USART) Clock Selection (Baud Rate generator Input Clock #define AT91C_US_CHRL_5_BITS ((unsigned int) 0x0 << 6) // (USART) Character Length: 5 bits #define AT91C_US_CHRL_6_BITS ((unsigned int) 0x1 << 6) // (USART) Character Length: 6 bits #define AT91C_US_CHRL_7_BITS ((unsigned int) 0x2 << 6) // (USART) Character Length: 7 bits #define AT91C_US_CHRL_8_BITS ((unsigned int) 0x3 << 6) // (USART) Character Length: 8 bits #define AT91C_US_SYNC ((unsigned int) 0x1 << 8) // (USART) Synchronous Mode Select #define AT91C_US_NBSTOP ((unsigned int) 0x3 << 12) // (USART) Number of Stop bits #define AT91C_US_NBSTOP_1_BIT ((unsigned int) 0x0 << 12) // (USART) 1 stop bit #define AT91C_US_NBSTOP_15_BIT ((unsigned int) 0x1 << 12) // (USART) Asynchronous (SYNC=0) 2 stop bits Synchronous (SYNC=1) 2 stop bits #define AT91C_US_NBSTOP_2_BIT ((unsigned int) 0x2 << 12) // (USART) 2 stop bits #define AT91C_US_MSBF ((unsigned int) 0x1 << 16) // (USART) Bit Order #define AT91C_US_MODE9 ((unsigned int) 0x1 << 17) // (USART) 9-bit Character length #define AT91C_US_CKLO ((unsigned int) 0x1 << 18) // (USART) Clock Output Select #define AT91C_US_OVER ((unsigned int) 0x1 << 19) // (USART) Over Sampling Mode #define AT91C_US_INACK ((unsigned int) 0x1 << 20) // (USART) Inhibit Non Acknowledge #define AT91C_US_DSNACK ((unsigned int) 0x1 << 21) // (USART) Disable Successive NACK #define AT91C_US_MAX_ITER ((unsigned int) 0x1 << 24) // (USART) Number of Repetitions #define AT91C_US_FILTER ((unsigned int) 0x1 << 28) // (USART) Receive Line Filter // -------- US_IER : (USART Offset: 0x8) Debug Unit Interrupt Enable Register -------- #define AT91C_US_RXBRK ((unsigned int) 0x1 << 2) // (USART) Break Received/End of Break #define AT91C_US_TIMEOUT ((unsigned int) 0x1 << 8) // (USART) Receiver Time-out #define AT91C_US_ITERATION ((unsigned int) 0x1 << 10) // (USART) Max number of Repetitions Reached #define AT91C_US_NACK ((unsigned int) 0x1 << 13) // (USART) Non Acknowledge #define AT91C_US_RIIC ((unsigned int) 0x1 << 16) // (USART) Ring INdicator Input Change Flag #define AT91C_US_DSRIC ((unsigned int) 0x1 << 17) // (USART) Data Set Ready Input Change Flag #define AT91C_US_DCDIC ((unsigned int) 0x1 << 18) // (USART) Data Carrier Flag #define AT91C_US_CTSIC ((unsigned int) 0x1 << 19) // (USART) Clear To Send Input Change Flag // -------- US_IDR : (USART Offset: 0xc) Debug Unit Interrupt Disable Register -------- // -------- US_IMR : (USART Offset: 0x10) Debug Unit Interrupt Mask Register -------- // -------- US_CSR : (USART Offset: 0x14) Debug Unit Channel Status Register -------- #define AT91C_US_RI ((unsigned int) 0x1 << 20) // (USART) Image of RI Input #define AT91C_US_DSR ((unsigned int) 0x1 << 21) // (USART) Image of DSR Input #define AT91C_US_DCD ((unsigned int) 0x1 << 22) // (USART) Image of DCD Input #define AT91C_US_CTS ((unsigned int) 0x1 << 23) // (USART) Image of CTS Input // ***************************************************************************** // SOFTWARE API DEFINITION FOR Two-wire Interface // ***************************************************************************** typedef struct _AT91S_TWI { AT91_REG TWI_CR; // Control Register AT91_REG TWI_MMR; // Master Mode Register AT91_REG Reserved0[1]; // AT91_REG TWI_IADR; // Internal Address Register AT91_REG TWI_CWGR; // Clock Waveform Generator Register AT91_REG Reserved1[3]; // AT91_REG TWI_SR; // Status Register AT91_REG TWI_IER; // Interrupt Enable Register AT91_REG TWI_IDR; // Interrupt Disable Register AT91_REG TWI_IMR; // Interrupt Mask Register AT91_REG TWI_RHR; // Receive Holding Register AT91_REG TWI_THR; // Transmit Holding Register } AT91S_TWI, *AT91PS_TWI; // -------- TWI_CR : (TWI Offset: 0x0) TWI Control Register -------- #define AT91C_TWI_START ((unsigned int) 0x1 << 0) // (TWI) Send a START Condition #define AT91C_TWI_STOP ((unsigned int) 0x1 << 1) // (TWI) Send a STOP Condition #define AT91C_TWI_MSEN ((unsigned int) 0x1 << 2) // (TWI) TWI Master Transfer Enabled #define AT91C_TWI_MSDIS ((unsigned int) 0x1 << 3) // (TWI) TWI Master Transfer Disabled #define AT91C_TWI_SWRST ((unsigned int) 0x1 << 7) // (TWI) Software Reset // -------- TWI_MMR : (TWI Offset: 0x4) TWI Master Mode Register -------- #define AT91C_TWI_IADRSZ ((unsigned int) 0x3 << 8) // (TWI) Internal Device Address Size #define AT91C_TWI_IADRSZ_NO ((unsigned int) 0x0 << 8) // (TWI) No internal device address #define AT91C_TWI_IADRSZ_1_BYTE ((unsigned int) 0x1 << 8) // (TWI) One-byte internal device address #define AT91C_TWI_IADRSZ_2_BYTE ((unsigned int) 0x2 << 8) // (TWI) Two-byte internal device address #define AT91C_TWI_IADRSZ_3_BYTE ((unsigned int) 0x3 << 8) // (TWI) Three-byte internal device address #define AT91C_TWI_MREAD ((unsigned int) 0x1 << 12) // (TWI) Master Read Direction #define AT91C_TWI_DADR ((unsigned int) 0x7F << 16) // (TWI) Device Address // -------- TWI_CWGR : (TWI Offset: 0x10) TWI Clock Waveform Generator Register -------- #define AT91C_TWI_CLDIV ((unsigned int) 0xFF << 0) // (TWI) Clock Low Divider #define AT91C_TWI_CHDIV ((unsigned int) 0xFF << 8) // (TWI) Clock High Divider #define AT91C_TWI_CKDIV ((unsigned int) 0x7 << 16) // (TWI) Clock Divider // -------- TWI_SR : (TWI Offset: 0x20) TWI Status Register -------- #define AT91C_TWI_TXCOMP ((unsigned int) 0x1 << 0) // (TWI) Transmission Completed #define AT91C_TWI_RXRDY ((unsigned int) 0x1 << 1) // (TWI) Receive holding register ReaDY #define AT91C_TWI_TXRDY ((unsigned int) 0x1 << 2) // (TWI) Transmit holding register ReaDY #define AT91C_TWI_OVRE ((unsigned int) 0x1 << 6) // (TWI) Overrun Error #define AT91C_TWI_UNRE ((unsigned int) 0x1 << 7) // (TWI) Underrun Error #define AT91C_TWI_NACK ((unsigned int) 0x1 << 8) // (TWI) Not Acknowledged // -------- TWI_IER : (TWI Offset: 0x24) TWI Interrupt Enable Register -------- // -------- TWI_IDR : (TWI Offset: 0x28) TWI Interrupt Disable Register -------- // -------- TWI_IMR : (TWI Offset: 0x2c) TWI Interrupt Mask Register -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR Timer Counter Channel Interface // ***************************************************************************** typedef struct _AT91S_TC { AT91_REG TC_CCR; // Channel Control Register AT91_REG TC_CMR; // Channel Mode Register (Capture Mode / Waveform Mode) AT91_REG Reserved0[2]; // AT91_REG TC_CV; // Counter Value AT91_REG TC_RA; // Register A AT91_REG TC_RB; // Register B AT91_REG TC_RC; // Register C AT91_REG TC_SR; // Status Register AT91_REG TC_IER; // Interrupt Enable Register AT91_REG TC_IDR; // Interrupt Disable Register AT91_REG TC_IMR; // Interrupt Mask Register } AT91S_TC, *AT91PS_TC; // -------- TC_CCR : (TC Offset: 0x0) TC Channel Control Register -------- #define AT91C_TC_CLKEN ((unsigned int) 0x1 << 0) // (TC) Counter Clock Enable Command #define AT91C_TC_CLKDIS ((unsigned int) 0x1 << 1) // (TC) Counter Clock Disable Command #define AT91C_TC_SWTRG ((unsigned int) 0x1 << 2) // (TC) Software Trigger Command // -------- TC_CMR : (TC Offset: 0x4) TC Channel Mode Register: Capture Mode / Waveform Mode -------- #define AT91C_TC_CLKS ((unsigned int) 0x7 << 0) // (TC) Clock Selection #define AT91C_TC_CLKS_TIMER_DIV1_CLOCK ((unsigned int) 0x0) // (TC) Clock selected: TIMER_DIV1_CLOCK #define AT91C_TC_CLKS_TIMER_DIV2_CLOCK ((unsigned int) 0x1) // (TC) Clock selected: TIMER_DIV2_CLOCK #define AT91C_TC_CLKS_TIMER_DIV3_CLOCK ((unsigned int) 0x2) // (TC) Clock selected: TIMER_DIV3_CLOCK #define AT91C_TC_CLKS_TIMER_DIV4_CLOCK ((unsigned int) 0x3) // (TC) Clock selected: TIMER_DIV4_CLOCK #define AT91C_TC_CLKS_TIMER_DIV5_CLOCK ((unsigned int) 0x4) // (TC) Clock selected: TIMER_DIV5_CLOCK #define AT91C_TC_CLKS_XC0 ((unsigned int) 0x5) // (TC) Clock selected: XC0 #define AT91C_TC_CLKS_XC1 ((unsigned int) 0x6) // (TC) Clock selected: XC1 #define AT91C_TC_CLKS_XC2 ((unsigned int) 0x7) // (TC) Clock selected: XC2 #define AT91C_TC_CLKI ((unsigned int) 0x1 << 3) // (TC) Clock Invert #define AT91C_TC_BURST ((unsigned int) 0x3 << 4) // (TC) Burst Signal Selection #define AT91C_TC_BURST_NONE ((unsigned int) 0x0 << 4) // (TC) The clock is not gated by an external signal #define AT91C_TC_BURST_XC0 ((unsigned int) 0x1 << 4) // (TC) XC0 is ANDed with the selected clock #define AT91C_TC_BURST_XC1 ((unsigned int) 0x2 << 4) // (TC) XC1 is ANDed with the selected clock #define AT91C_TC_BURST_XC2 ((unsigned int) 0x3 << 4) // (TC) XC2 is ANDed with the selected clock #define AT91C_TC_CPCSTOP ((unsigned int) 0x1 << 6) // (TC) Counter Clock Stopped with RC Compare #define AT91C_TC_LDBSTOP ((unsigned int) 0x1 << 6) // (TC) Counter Clock Stopped with RB Loading #define AT91C_TC_CPCDIS ((unsigned int) 0x1 << 7) // (TC) Counter Clock Disable with RC Compare #define AT91C_TC_LDBDIS ((unsigned int) 0x1 << 7) // (TC) Counter Clock Disabled with RB Loading #define AT91C_TC_ETRGEDG ((unsigned int) 0x3 << 8) // (TC) External Trigger Edge Selection #define AT91C_TC_ETRGEDG_NONE ((unsigned int) 0x0 << 8) // (TC) Edge: None #define AT91C_TC_ETRGEDG_RISING ((unsigned int) 0x1 << 8) // (TC) Edge: rising edge #define AT91C_TC_ETRGEDG_FALLING ((unsigned int) 0x2 << 8) // (TC) Edge: falling edge #define AT91C_TC_ETRGEDG_BOTH ((unsigned int) 0x3 << 8) // (TC) Edge: each edge #define AT91C_TC_EEVTEDG ((unsigned int) 0x3 << 8) // (TC) External Event Edge Selection #define AT91C_TC_EEVTEDG_NONE ((unsigned int) 0x0 << 8) // (TC) Edge: None #define AT91C_TC_EEVTEDG_RISING ((unsigned int) 0x1 << 8) // (TC) Edge: rising edge #define AT91C_TC_EEVTEDG_FALLING ((unsigned int) 0x2 << 8) // (TC) Edge: falling edge #define AT91C_TC_EEVTEDG_BOTH ((unsigned int) 0x3 << 8) // (TC) Edge: each edge #define AT91C_TC_EEVT ((unsigned int) 0x3 << 10) // (TC) External Event Selection #define AT91C_TC_EEVT_TIOB ((unsigned int) 0x0 << 10) // (TC) Signal selected as external event: TIOB TIOB direction: input #define AT91C_TC_EEVT_XC0 ((unsigned int) 0x1 << 10) // (TC) Signal selected as external event: XC0 TIOB direction: output #define AT91C_TC_EEVT_XC1 ((unsigned int) 0x2 << 10) // (TC) Signal selected as external event: XC1 TIOB direction: output #define AT91C_TC_EEVT_XC2 ((unsigned int) 0x3 << 10) // (TC) Signal selected as external event: XC2 TIOB direction: output #define AT91C_TC_ABETRG ((unsigned int) 0x1 << 10) // (TC) TIOA or TIOB External Trigger Selection #define AT91C_TC_ENETRG ((unsigned int) 0x1 << 12) // (TC) External Event Trigger enable #define AT91C_TC_WAVESEL ((unsigned int) 0x3 << 13) // (TC) Waveform Selection #define AT91C_TC_WAVESEL_UP ((unsigned int) 0x0 << 13) // (TC) UP mode without atomatic trigger on RC Compare #define AT91C_TC_WAVESEL_UPDOWN ((unsigned int) 0x1 << 13) // (TC) UPDOWN mode without automatic trigger on RC Compare #define AT91C_TC_WAVESEL_UP_AUTO ((unsigned int) 0x2 << 13) // (TC) UP mode with automatic trigger on RC Compare #define AT91C_TC_WAVESEL_UPDOWN_AUTO ((unsigned int) 0x3 << 13) // (TC) UPDOWN mode with automatic trigger on RC Compare #define AT91C_TC_CPCTRG ((unsigned int) 0x1 << 14) // (TC) RC Compare Trigger Enable #define AT91C_TC_WAVE ((unsigned int) 0x1 << 15) // (TC) #define AT91C_TC_ACPA ((unsigned int) 0x3 << 16) // (TC) RA Compare Effect on TIOA #define AT91C_TC_ACPA_NONE ((unsigned int) 0x0 << 16) // (TC) Effect: none #define AT91C_TC_ACPA_SET ((unsigned int) 0x1 << 16) // (TC) Effect: set #define AT91C_TC_ACPA_CLEAR ((unsigned int) 0x2 << 16) // (TC) Effect: clear #define AT91C_TC_ACPA_TOGGLE ((unsigned int) 0x3 << 16) // (TC) Effect: toggle #define AT91C_TC_LDRA ((unsigned int) 0x3 << 16) // (TC) RA Loading Selection #define AT91C_TC_LDRA_NONE ((unsigned int) 0x0 << 16) // (TC) Edge: None #define AT91C_TC_LDRA_RISING ((unsigned int) 0x1 << 16) // (TC) Edge: rising edge of TIOA #define AT91C_TC_LDRA_FALLING ((unsigned int) 0x2 << 16) // (TC) Edge: falling edge of TIOA #define AT91C_TC_LDRA_BOTH ((unsigned int) 0x3 << 16) // (TC) Edge: each edge of TIOA #define AT91C_TC_ACPC ((unsigned int) 0x3 << 18) // (TC) RC Compare Effect on TIOA #define AT91C_TC_ACPC_NONE ((unsigned int) 0x0 << 18) // (TC) Effect: none #define AT91C_TC_ACPC_SET ((unsigned int) 0x1 << 18) // (TC) Effect: set #define AT91C_TC_ACPC_CLEAR ((unsigned int) 0x2 << 18) // (TC) Effect: clear #define AT91C_TC_ACPC_TOGGLE ((unsigned int) 0x3 << 18) // (TC) Effect: toggle #define AT91C_TC_LDRB ((unsigned int) 0x3 << 18) // (TC) RB Loading Selection #define AT91C_TC_LDRB_NONE ((unsigned int) 0x0 << 18) // (TC) Edge: None #define AT91C_TC_LDRB_RISING ((unsigned int) 0x1 << 18) // (TC) Edge: rising edge of TIOA #define AT91C_TC_LDRB_FALLING ((unsigned int) 0x2 << 18) // (TC) Edge: falling edge of TIOA #define AT91C_TC_LDRB_BOTH ((unsigned int) 0x3 << 18) // (TC) Edge: each edge of TIOA #define AT91C_TC_AEEVT ((unsigned int) 0x3 << 20) // (TC) External Event Effect on TIOA #define AT91C_TC_AEEVT_NONE ((unsigned int) 0x0 << 20) // (TC) Effect: none #define AT91C_TC_AEEVT_SET ((unsigned int) 0x1 << 20) // (TC) Effect: set #define AT91C_TC_AEEVT_CLEAR ((unsigned int) 0x2 << 20) // (TC) Effect: clear #define AT91C_TC_AEEVT_TOGGLE ((unsigned int) 0x3 << 20) // (TC) Effect: toggle #define AT91C_TC_ASWTRG ((unsigned int) 0x3 << 22) // (TC) Software Trigger Effect on TIOA #define AT91C_TC_ASWTRG_NONE ((unsigned int) 0x0 << 22) // (TC) Effect: none #define AT91C_TC_ASWTRG_SET ((unsigned int) 0x1 << 22) // (TC) Effect: set #define AT91C_TC_ASWTRG_CLEAR ((unsigned int) 0x2 << 22) // (TC) Effect: clear #define AT91C_TC_ASWTRG_TOGGLE ((unsigned int) 0x3 << 22) // (TC) Effect: toggle #define AT91C_TC_BCPB ((unsigned int) 0x3 << 24) // (TC) RB Compare Effect on TIOB #define AT91C_TC_BCPB_NONE ((unsigned int) 0x0 << 24) // (TC) Effect: none #define AT91C_TC_BCPB_SET ((unsigned int) 0x1 << 24) // (TC) Effect: set #define AT91C_TC_BCPB_CLEAR ((unsigned int) 0x2 << 24) // (TC) Effect: clear #define AT91C_TC_BCPB_TOGGLE ((unsigned int) 0x3 << 24) // (TC) Effect: toggle #define AT91C_TC_BCPC ((unsigned int) 0x3 << 26) // (TC) RC Compare Effect on TIOB #define AT91C_TC_BCPC_NONE ((unsigned int) 0x0 << 26) // (TC) Effect: none #define AT91C_TC_BCPC_SET ((unsigned int) 0x1 << 26) // (TC) Effect: set #define AT91C_TC_BCPC_CLEAR ((unsigned int) 0x2 << 26) // (TC) Effect: clear #define AT91C_TC_BCPC_TOGGLE ((unsigned int) 0x3 << 26) // (TC) Effect: toggle #define AT91C_TC_BEEVT ((unsigned int) 0x3 << 28) // (TC) External Event Effect on TIOB #define AT91C_TC_BEEVT_NONE ((unsigned int) 0x0 << 28) // (TC) Effect: none #define AT91C_TC_BEEVT_SET ((unsigned int) 0x1 << 28) // (TC) Effect: set #define AT91C_TC_BEEVT_CLEAR ((unsigned int) 0x2 << 28) // (TC) Effect: clear #define AT91C_TC_BEEVT_TOGGLE ((unsigned int) 0x3 << 28) // (TC) Effect: toggle #define AT91C_TC_BSWTRG ((unsigned int) 0x3 << 30) // (TC) Software Trigger Effect on TIOB #define AT91C_TC_BSWTRG_NONE ((unsigned int) 0x0 << 30) // (TC) Effect: none #define AT91C_TC_BSWTRG_SET ((unsigned int) 0x1 << 30) // (TC) Effect: set #define AT91C_TC_BSWTRG_CLEAR ((unsigned int) 0x2 << 30) // (TC) Effect: clear #define AT91C_TC_BSWTRG_TOGGLE ((unsigned int) 0x3 << 30) // (TC) Effect: toggle // -------- TC_SR : (TC Offset: 0x20) TC Channel Status Register -------- #define AT91C_TC_COVFS ((unsigned int) 0x1 << 0) // (TC) Counter Overflow #define AT91C_TC_LOVRS ((unsigned int) 0x1 << 1) // (TC) Load Overrun #define AT91C_TC_CPAS ((unsigned int) 0x1 << 2) // (TC) RA Compare #define AT91C_TC_CPBS ((unsigned int) 0x1 << 3) // (TC) RB Compare #define AT91C_TC_CPCS ((unsigned int) 0x1 << 4) // (TC) RC Compare #define AT91C_TC_LDRAS ((unsigned int) 0x1 << 5) // (TC) RA Loading #define AT91C_TC_LDRBS ((unsigned int) 0x1 << 6) // (TC) RB Loading #define AT91C_TC_ETRGS ((unsigned int) 0x1 << 7) // (TC) External Trigger #define AT91C_TC_CLKSTA ((unsigned int) 0x1 << 16) // (TC) Clock Enabling #define AT91C_TC_MTIOA ((unsigned int) 0x1 << 17) // (TC) TIOA Mirror #define AT91C_TC_MTIOB ((unsigned int) 0x1 << 18) // (TC) TIOA Mirror // -------- TC_IER : (TC Offset: 0x24) TC Channel Interrupt Enable Register -------- // -------- TC_IDR : (TC Offset: 0x28) TC Channel Interrupt Disable Register -------- // -------- TC_IMR : (TC Offset: 0x2c) TC Channel Interrupt Mask Register -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR Timer Counter Interface // ***************************************************************************** typedef struct _AT91S_TCB { AT91S_TC TCB_TC0; // TC Channel 0 AT91_REG Reserved0[4]; // AT91S_TC TCB_TC1; // TC Channel 1 AT91_REG Reserved1[4]; // AT91S_TC TCB_TC2; // TC Channel 2 AT91_REG Reserved2[4]; // AT91_REG TCB_BCR; // TC Block Control Register AT91_REG TCB_BMR; // TC Block Mode Register } AT91S_TCB, *AT91PS_TCB; // -------- TCB_BCR : (TCB Offset: 0xc0) TC Block Control Register -------- #define AT91C_TCB_SYNC ((unsigned int) 0x1 << 0) // (TCB) Synchro Command // -------- TCB_BMR : (TCB Offset: 0xc4) TC Block Mode Register -------- #define AT91C_TCB_TC0XC0S ((unsigned int) 0x3 << 0) // (TCB) External Clock Signal 0 Selection #define AT91C_TCB_TC0XC0S_TCLK0 ((unsigned int) 0x0) // (TCB) TCLK0 connected to XC0 #define AT91C_TCB_TC0XC0S_NONE ((unsigned int) 0x1) // (TCB) None signal connected to XC0 #define AT91C_TCB_TC0XC0S_TIOA1 ((unsigned int) 0x2) // (TCB) TIOA1 connected to XC0 #define AT91C_TCB_TC0XC0S_TIOA2 ((unsigned int) 0x3) // (TCB) TIOA2 connected to XC0 #define AT91C_TCB_TC1XC1S ((unsigned int) 0x3 << 2) // (TCB) External Clock Signal 1 Selection #define AT91C_TCB_TC1XC1S_TCLK1 ((unsigned int) 0x0 << 2) // (TCB) TCLK1 connected to XC1 #define AT91C_TCB_TC1XC1S_NONE ((unsigned int) 0x1 << 2) // (TCB) None signal connected to XC1 #define AT91C_TCB_TC1XC1S_TIOA0 ((unsigned int) 0x2 << 2) // (TCB) TIOA0 connected to XC1 #define AT91C_TCB_TC1XC1S_TIOA2 ((unsigned int) 0x3 << 2) // (TCB) TIOA2 connected to XC1 #define AT91C_TCB_TC2XC2S ((unsigned int) 0x3 << 4) // (TCB) External Clock Signal 2 Selection #define AT91C_TCB_TC2XC2S_TCLK2 ((unsigned int) 0x0 << 4) // (TCB) TCLK2 connected to XC2 #define AT91C_TCB_TC2XC2S_NONE ((unsigned int) 0x1 << 4) // (TCB) None signal connected to XC2 #define AT91C_TCB_TC2XC2S_TIOA0 ((unsigned int) 0x2 << 4) // (TCB) TIOA0 connected to XC2 #define AT91C_TCB_TC2XC2S_TIOA1 ((unsigned int) 0x3 << 4) // (TCB) TIOA2 connected to XC2 // ***************************************************************************** // SOFTWARE API DEFINITION FOR PWMC Channel Interface // ***************************************************************************** typedef struct _AT91S_PWMC_CH { AT91_REG PWMC_CMR; // Channel Mode Register AT91_REG PWMC_CDTYR; // Channel Duty Cycle Register AT91_REG PWMC_CPRDR; // Channel Period Register AT91_REG PWMC_CCNTR; // Channel Counter Register AT91_REG PWMC_CUPDR; // Channel Update Register AT91_REG PWMC_Reserved[3]; // Reserved } AT91S_PWMC_CH, *AT91PS_PWMC_CH; // -------- PWMC_CMR : (PWMC_CH Offset: 0x0) PWMC Channel Mode Register -------- #define AT91C_PWMC_CPRE ((unsigned int) 0xF << 0) // (PWMC_CH) Channel Pre-scaler : PWMC_CLKx #define AT91C_PWMC_CPRE_MCK ((unsigned int) 0x0) // (PWMC_CH) #define AT91C_PWMC_CPRE_MCKA ((unsigned int) 0xB) // (PWMC_CH) #define AT91C_PWMC_CPRE_MCKB ((unsigned int) 0xC) // (PWMC_CH) #define AT91C_PWMC_CALG ((unsigned int) 0x1 << 8) // (PWMC_CH) Channel Alignment #define AT91C_PWMC_CPOL ((unsigned int) 0x1 << 9) // (PWMC_CH) Channel Polarity #define AT91C_PWMC_CPD ((unsigned int) 0x1 << 10) // (PWMC_CH) Channel Update Period // -------- PWMC_CDTYR : (PWMC_CH Offset: 0x4) PWMC Channel Duty Cycle Register -------- #define AT91C_PWMC_CDTY ((unsigned int) 0x0 << 0) // (PWMC_CH) Channel Duty Cycle // -------- PWMC_CPRDR : (PWMC_CH Offset: 0x8) PWMC Channel Period Register -------- #define AT91C_PWMC_CPRD ((unsigned int) 0x0 << 0) // (PWMC_CH) Channel Period // -------- PWMC_CCNTR : (PWMC_CH Offset: 0xc) PWMC Channel Counter Register -------- #define AT91C_PWMC_CCNT ((unsigned int) 0x0 << 0) // (PWMC_CH) Channel Counter // -------- PWMC_CUPDR : (PWMC_CH Offset: 0x10) PWMC Channel Update Register -------- #define AT91C_PWMC_CUPD ((unsigned int) 0x0 << 0) // (PWMC_CH) Channel Update // ***************************************************************************** // SOFTWARE API DEFINITION FOR Pulse Width Modulation Controller Interface // ***************************************************************************** typedef struct _AT91S_PWMC { AT91_REG PWMC_MR; // PWMC Mode Register AT91_REG PWMC_ENA; // PWMC Enable Register AT91_REG PWMC_DIS; // PWMC Disable Register AT91_REG PWMC_SR; // PWMC Status Register AT91_REG PWMC_IER; // PWMC Interrupt Enable Register AT91_REG PWMC_IDR; // PWMC Interrupt Disable Register AT91_REG PWMC_IMR; // PWMC Interrupt Mask Register AT91_REG PWMC_ISR; // PWMC Interrupt Status Register AT91_REG Reserved0[55]; // AT91_REG PWMC_VR; // PWMC Version Register AT91_REG Reserved1[64]; // AT91S_PWMC_CH PWMC_CH[32]; // PWMC Channel 0 } AT91S_PWMC, *AT91PS_PWMC; // -------- PWMC_MR : (PWMC Offset: 0x0) PWMC Mode Register -------- #define AT91C_PWMC_DIVA ((unsigned int) 0xFF << 0) // (PWMC) CLKA divide factor. #define AT91C_PWMC_PREA ((unsigned int) 0xF << 8) // (PWMC) Divider Input Clock Prescaler A #define AT91C_PWMC_PREA_MCK ((unsigned int) 0x0 << 8) // (PWMC) #define AT91C_PWMC_DIVB ((unsigned int) 0xFF << 16) // (PWMC) CLKB divide factor. #define AT91C_PWMC_PREB ((unsigned int) 0xF << 24) // (PWMC) Divider Input Clock Prescaler B #define AT91C_PWMC_PREB_MCK ((unsigned int) 0x0 << 24) // (PWMC) // -------- PWMC_ENA : (PWMC Offset: 0x4) PWMC Enable Register -------- #define AT91C_PWMC_CHID0 ((unsigned int) 0x1 << 0) // (PWMC) Channel ID 0 #define AT91C_PWMC_CHID1 ((unsigned int) 0x1 << 1) // (PWMC) Channel ID 1 #define AT91C_PWMC_CHID2 ((unsigned int) 0x1 << 2) // (PWMC) Channel ID 2 #define AT91C_PWMC_CHID3 ((unsigned int) 0x1 << 3) // (PWMC) Channel ID 3 #define AT91C_PWMC_CHID4 ((unsigned int) 0x1 << 4) // (PWMC) Channel ID 4 #define AT91C_PWMC_CHID5 ((unsigned int) 0x1 << 5) // (PWMC) Channel ID 5 #define AT91C_PWMC_CHID6 ((unsigned int) 0x1 << 6) // (PWMC) Channel ID 6 #define AT91C_PWMC_CHID7 ((unsigned int) 0x1 << 7) // (PWMC) Channel ID 7 // -------- PWMC_DIS : (PWMC Offset: 0x8) PWMC Disable Register -------- // -------- PWMC_SR : (PWMC Offset: 0xc) PWMC Status Register -------- // -------- PWMC_IER : (PWMC Offset: 0x10) PWMC Interrupt Enable Register -------- // -------- PWMC_IDR : (PWMC Offset: 0x14) PWMC Interrupt Disable Register -------- // -------- PWMC_IMR : (PWMC Offset: 0x18) PWMC Interrupt Mask Register -------- // -------- PWMC_ISR : (PWMC Offset: 0x1c) PWMC Interrupt Status Register -------- // ***************************************************************************** // SOFTWARE API DEFINITION FOR USB Device Interface // ***************************************************************************** typedef struct _AT91S_UDP { AT91_REG UDP_NUM; // Frame Number Register AT91_REG UDP_GLBSTATE; // Global State Register AT91_REG UDP_FADDR; // Function Address Register AT91_REG Reserved0[1]; // AT91_REG UDP_IER; // Interrupt Enable Register AT91_REG UDP_IDR; // Interrupt Disable Register AT91_REG UDP_IMR; // Interrupt Mask Register AT91_REG UDP_ISR; // Interrupt Status Register AT91_REG UDP_ICR; // Interrupt Clear Register AT91_REG Reserved1[1]; // AT91_REG UDP_RSTEP; // Reset Endpoint Register AT91_REG Reserved2[1]; // AT91_REG UDP_CSR[8]; // Endpoint Control and Status Register AT91_REG UDP_FDR[8]; // Endpoint FIFO Data Register AT91_REG Reserved3[1]; // AT91_REG UDP_TXVC; // Transceiver Control Register } AT91S_UDP, *AT91PS_UDP; // -------- UDP_FRM_NUM : (UDP Offset: 0x0) USB Frame Number Register -------- #define AT91C_UDP_FRM_NUM ((unsigned int) 0x7FF << 0) // (UDP) Frame Number as Defined in the Packet Field Formats #define AT91C_UDP_FRM_ERR ((unsigned int) 0x1 << 16) // (UDP) Frame Error #define AT91C_UDP_FRM_OK ((unsigned int) 0x1 << 17) // (UDP) Frame OK // -------- UDP_GLB_STATE : (UDP Offset: 0x4) USB Global State Register -------- #define AT91C_UDP_FADDEN ((unsigned int) 0x1 << 0) // (UDP) Function Address Enable #define AT91C_UDP_CONFG ((unsigned int) 0x1 << 1) // (UDP) Configured #define AT91C_UDP_ESR ((unsigned int) 0x1 << 2) // (UDP) Enable Send Resume #define AT91C_UDP_RSMINPR ((unsigned int) 0x1 << 3) // (UDP) A Resume Has Been Sent to the Host #define AT91C_UDP_RMWUPE ((unsigned int) 0x1 << 4) // (UDP) Remote Wake Up Enable // -------- UDP_FADDR : (UDP Offset: 0x8) USB Function Address Register -------- #define AT91C_UDP_FADD ((unsigned int) 0xFF << 0) // (UDP) Function Address Value #define AT91C_UDP_FEN ((unsigned int) 0x1 << 8) // (UDP) Function Enable // -------- UDP_IER : (UDP Offset: 0x10) USB Interrupt Enable Register -------- #define AT91C_UDP_EPINT0 ((unsigned int) 0x1 << 0) // (UDP) Endpoint 0 Interrupt #define AT91C_UDP_EPINT1 ((unsigned int) 0x1 << 1) // (UDP) Endpoint 0 Interrupt #define AT91C_UDP_EPINT2 ((unsigned int) 0x1 << 2) // (UDP) Endpoint 2 Interrupt #define AT91C_UDP_EPINT3 ((unsigned int) 0x1 << 3) // (UDP) Endpoint 3 Interrupt #define AT91C_UDP_EPINT4 ((unsigned int) 0x1 << 4) // (UDP) Endpoint 4 Interrupt #define AT91C_UDP_EPINT5 ((unsigned int) 0x1 << 5) // (UDP) Endpoint 5 Interrupt #define AT91C_UDP_EPINT6 ((unsigned int) 0x1 << 6) // (UDP) Endpoint 6 Interrupt #define AT91C_UDP_EPINT7 ((unsigned int) 0x1 << 7) // (UDP) Endpoint 7 Interrupt #define AT91C_UDP_RXSUSP ((unsigned int) 0x1 << 8) // (UDP) USB Suspend Interrupt #define AT91C_UDP_RXRSM ((unsigned int) 0x1 << 9) // (UDP) USB Resume Interrupt #define AT91C_UDP_EXTRSM ((unsigned int) 0x1 << 10) // (UDP) USB External Resume Interrupt #define AT91C_UDP_SOFINT ((unsigned int) 0x1 << 11) // (UDP) USB Start Of frame Interrupt #define AT91C_UDP_WAKEUP ((unsigned int) 0x1 << 13) // (UDP) USB Resume Interrupt // -------- UDP_IDR : (UDP Offset: 0x14) USB Interrupt Disable Register -------- // -------- UDP_IMR : (UDP Offset: 0x18) USB Interrupt Mask Register -------- // -------- UDP_ISR : (UDP Offset: 0x1c) USB Interrupt Status Register -------- #define AT91C_UDP_ENDBUSRES ((unsigned int) 0x1 << 12) // (UDP) USB End Of Bus Reset Interrupt // -------- UDP_ICR : (UDP Offset: 0x20) USB Interrupt Clear Register -------- // -------- UDP_RST_EP : (UDP Offset: 0x28) USB Reset Endpoint Register -------- #define AT91C_UDP_EP0 ((unsigned int) 0x1 << 0) // (UDP) Reset Endpoint 0 #define AT91C_UDP_EP1 ((unsigned int) 0x1 << 1) // (UDP) Reset Endpoint 1 #define AT91C_UDP_EP2 ((unsigned int) 0x1 << 2) // (UDP) Reset Endpoint 2 #define AT91C_UDP_EP3 ((unsigned int) 0x1 << 3) // (UDP) Reset Endpoint 3 #define AT91C_UDP_EP4 ((unsigned int) 0x1 << 4) // (UDP) Reset Endpoint 4 #define AT91C_UDP_EP5 ((unsigned int) 0x1 << 5) // (UDP) Reset Endpoint 5 #define AT91C_UDP_EP6 ((unsigned int) 0x1 << 6) // (UDP) Reset Endpoint 6 #define AT91C_UDP_EP7 ((unsigned int) 0x1 << 7) // (UDP) Reset Endpoint 7 // -------- UDP_CSR : (UDP Offset: 0x30) USB Endpoint Control and Status Register -------- #define AT91C_UDP_TXCOMP ((unsigned int) 0x1 << 0) // (UDP) Generates an IN packet with data previously written in the DPR #define AT91C_UDP_RX_DATA_BK0 ((unsigned int) 0x1 << 1) // (UDP) Receive Data Bank 0 #define AT91C_UDP_RXSETUP ((unsigned int) 0x1 << 2) // (UDP) Sends STALL to the Host (Control endpoints) #define AT91C_UDP_ISOERROR ((unsigned int) 0x1 << 3) // (UDP) Isochronous error (Isochronous endpoints) #define AT91C_UDP_TXPKTRDY ((unsigned int) 0x1 << 4) // (UDP) Transmit Packet Ready #define AT91C_UDP_FORCESTALL ((unsigned int) 0x1 << 5) // (UDP) Force Stall (used by Control, Bulk and Isochronous endpoints). #define AT91C_UDP_RX_DATA_BK1 ((unsigned int) 0x1 << 6) // (UDP) Receive Data Bank 1 (only used by endpoints with ping-pong attributes). #define AT91C_UDP_DIR ((unsigned int) 0x1 << 7) // (UDP) Transfer Direction #define AT91C_UDP_EPTYPE ((unsigned int) 0x7 << 8) // (UDP) Endpoint type #define AT91C_UDP_EPTYPE_CTRL ((unsigned int) 0x0 << 8) // (UDP) Control #define AT91C_UDP_EPTYPE_ISO_OUT ((unsigned int) 0x1 << 8) // (UDP) Isochronous OUT #define AT91C_UDP_EPTYPE_BULK_OUT ((unsigned int) 0x2 << 8) // (UDP) Bulk OUT #define AT91C_UDP_EPTYPE_INT_OUT ((unsigned int) 0x3 << 8) // (UDP) Interrupt OUT #define AT91C_UDP_EPTYPE_ISO_IN ((unsigned int) 0x5 << 8) // (UDP) Isochronous IN #define AT91C_UDP_EPTYPE_BULK_IN ((unsigned int) 0x6 << 8) // (UDP) Bulk IN #define AT91C_UDP_EPTYPE_INT_IN ((unsigned int) 0x7 << 8) // (UDP) Interrupt IN #define AT91C_UDP_DTGLE ((unsigned int) 0x1 << 11) // (UDP) Data Toggle #define AT91C_UDP_EPEDS ((unsigned int) 0x1 << 15) // (UDP) Endpoint Enable Disable #define AT91C_UDP_RXBYTECNT ((unsigned int) 0x7FF << 16) // (UDP) Number Of Bytes Available in the FIFO // -------- UDP_TXVC : (UDP Offset: 0x74) Transceiver Control Register -------- #define AT91C_UDP_TXVDIS ((unsigned int) 0x1 << 8) // (UDP) #define AT91C_UDP_PUON ((unsigned int) 0x1 << 9) // (UDP) Pull-up ON // ***************************************************************************** // REGISTER ADDRESS DEFINITION FOR AT91SAM7S256 // ***************************************************************************** // ========== Register definition for SYS peripheral ========== // ========== Register definition for AIC peripheral ========== #define AT91C_AIC_IVR ((AT91_REG *) 0xFFFFF100) // (AIC) IRQ Vector Register #define AT91C_AIC_SMR ((AT91_REG *) 0xFFFFF000) // (AIC) Source Mode Register #define AT91C_AIC_FVR ((AT91_REG *) 0xFFFFF104) // (AIC) FIQ Vector Register #define AT91C_AIC_DCR ((AT91_REG *) 0xFFFFF138) // (AIC) Debug Control Register (Protect) #define AT91C_AIC_EOICR ((AT91_REG *) 0xFFFFF130) // (AIC) End of Interrupt Command Register #define AT91C_AIC_SVR ((AT91_REG *) 0xFFFFF080) // (AIC) Source Vector Register #define AT91C_AIC_FFSR ((AT91_REG *) 0xFFFFF148) // (AIC) Fast Forcing Status Register #define AT91C_AIC_ICCR ((AT91_REG *) 0xFFFFF128) // (AIC) Interrupt Clear Command Register #define AT91C_AIC_ISR ((AT91_REG *) 0xFFFFF108) // (AIC) Interrupt Status Register #define AT91C_AIC_IMR ((AT91_REG *) 0xFFFFF110) // (AIC) Interrupt Mask Register #define AT91C_AIC_IPR ((AT91_REG *) 0xFFFFF10C) // (AIC) Interrupt Pending Register #define AT91C_AIC_FFER ((AT91_REG *) 0xFFFFF140) // (AIC) Fast Forcing Enable Register #define AT91C_AIC_IECR ((AT91_REG *) 0xFFFFF120) // (AIC) Interrupt Enable Command Register #define AT91C_AIC_ISCR ((AT91_REG *) 0xFFFFF12C) // (AIC) Interrupt Set Command Register #define AT91C_AIC_FFDR ((AT91_REG *) 0xFFFFF144) // (AIC) Fast Forcing Disable Register #define AT91C_AIC_CISR ((AT91_REG *) 0xFFFFF114) // (AIC) Core Interrupt Status Register #define AT91C_AIC_IDCR ((AT91_REG *) 0xFFFFF124) // (AIC) Interrupt Disable Command Register #define AT91C_AIC_SPU ((AT91_REG *) 0xFFFFF134) // (AIC) Spurious Vector Register // ========== Register definition for PDC_DBGU peripheral ========== #define AT91C_DBGU_TCR ((AT91_REG *) 0xFFFFF30C) // (PDC_DBGU) Transmit Counter Register #define AT91C_DBGU_RNPR ((AT91_REG *) 0xFFFFF310) // (PDC_DBGU) Receive Next Pointer Register #define AT91C_DBGU_TNPR ((AT91_REG *) 0xFFFFF318) // (PDC_DBGU) Transmit Next Pointer Register #define AT91C_DBGU_TPR ((AT91_REG *) 0xFFFFF308) // (PDC_DBGU) Transmit Pointer Register #define AT91C_DBGU_RPR ((AT91_REG *) 0xFFFFF300) // (PDC_DBGU) Receive Pointer Register #define AT91C_DBGU_RCR ((AT91_REG *) 0xFFFFF304) // (PDC_DBGU) Receive Counter Register #define AT91C_DBGU_RNCR ((AT91_REG *) 0xFFFFF314) // (PDC_DBGU) Receive Next Counter Register #define AT91C_DBGU_PTCR ((AT91_REG *) 0xFFFFF320) // (PDC_DBGU) PDC Transfer Control Register #define AT91C_DBGU_PTSR ((AT91_REG *) 0xFFFFF324) // (PDC_DBGU) PDC Transfer Status Register #define AT91C_DBGU_TNCR ((AT91_REG *) 0xFFFFF31C) // (PDC_DBGU) Transmit Next Counter Register // ========== Register definition for DBGU peripheral ========== #define AT91C_DBGU_EXID ((AT91_REG *) 0xFFFFF244) // (DBGU) Chip ID Extension Register #define AT91C_DBGU_BRGR ((AT91_REG *) 0xFFFFF220) // (DBGU) Baud Rate Generator Register #define AT91C_DBGU_IDR ((AT91_REG *) 0xFFFFF20C) // (DBGU) Interrupt Disable Register #define AT91C_DBGU_CSR ((AT91_REG *) 0xFFFFF214) // (DBGU) Channel Status Register #define AT91C_DBGU_CIDR ((AT91_REG *) 0xFFFFF240) // (DBGU) Chip ID Register #define AT91C_DBGU_MR ((AT91_REG *) 0xFFFFF204) // (DBGU) Mode Register #define AT91C_DBGU_IMR ((AT91_REG *) 0xFFFFF210) // (DBGU) Interrupt Mask Register #define AT91C_DBGU_CR ((AT91_REG *) 0xFFFFF200) // (DBGU) Control Register #define AT91C_DBGU_FNTR ((AT91_REG *) 0xFFFFF248) // (DBGU) Force NTRST Register #define AT91C_DBGU_THR ((AT91_REG *) 0xFFFFF21C) // (DBGU) Transmitter Holding Register #define AT91C_DBGU_RHR ((AT91_REG *) 0xFFFFF218) // (DBGU) Receiver Holding Register #define AT91C_DBGU_IER ((AT91_REG *) 0xFFFFF208) // (DBGU) Interrupt Enable Register // ========== Register definition for PIOA peripheral ========== #define AT91C_PIOA_ODR ((AT91_REG *) 0xFFFFF414) // (PIOA) Output Disable Registerr #define AT91C_PIOA_SODR ((AT91_REG *) 0xFFFFF430) // (PIOA) Set Output Data Register #define AT91C_PIOA_ISR ((AT91_REG *) 0xFFFFF44C) // (PIOA) Interrupt Status Register #define AT91C_PIOA_ABSR ((AT91_REG *) 0xFFFFF478) // (PIOA) AB Select Status Register #define AT91C_PIOA_IER ((AT91_REG *) 0xFFFFF440) // (PIOA) Interrupt Enable Register #define AT91C_PIOA_PPUDR ((AT91_REG *) 0xFFFFF460) // (PIOA) Pull-up Disable Register #define AT91C_PIOA_IMR ((AT91_REG *) 0xFFFFF448) // (PIOA) Interrupt Mask Register #define AT91C_PIOA_PER ((AT91_REG *) 0xFFFFF400) // (PIOA) PIO Enable Register #define AT91C_PIOA_IFDR ((AT91_REG *) 0xFFFFF424) // (PIOA) Input Filter Disable Register #define AT91C_PIOA_OWDR ((AT91_REG *) 0xFFFFF4A4) // (PIOA) Output Write Disable Register #define AT91C_PIOA_MDSR ((AT91_REG *) 0xFFFFF458) // (PIOA) Multi-driver Status Register #define AT91C_PIOA_IDR ((AT91_REG *) 0xFFFFF444) // (PIOA) Interrupt Disable Register #define AT91C_PIOA_ODSR ((AT91_REG *) 0xFFFFF438) // (PIOA) Output Data Status Register #define AT91C_PIOA_PPUSR ((AT91_REG *) 0xFFFFF468) // (PIOA) Pull-up Status Register #define AT91C_PIOA_OWSR ((AT91_REG *) 0xFFFFF4A8) // (PIOA) Output Write Status Register #define AT91C_PIOA_BSR ((AT91_REG *) 0xFFFFF474) // (PIOA) Select B Register #define AT91C_PIOA_OWER ((AT91_REG *) 0xFFFFF4A0) // (PIOA) Output Write Enable Register #define AT91C_PIOA_IFER ((AT91_REG *) 0xFFFFF420) // (PIOA) Input Filter Enable Register #define AT91C_PIOA_PDSR ((AT91_REG *) 0xFFFFF43C) // (PIOA) Pin Data Status Register #define AT91C_PIOA_PPUER ((AT91_REG *) 0xFFFFF464) // (PIOA) Pull-up Enable Register #define AT91C_PIOA_OSR ((AT91_REG *) 0xFFFFF418) // (PIOA) Output Status Register #define AT91C_PIOA_ASR ((AT91_REG *) 0xFFFFF470) // (PIOA) Select A Register #define AT91C_PIOA_MDDR ((AT91_REG *) 0xFFFFF454) // (PIOA) Multi-driver Disable Register #define AT91C_PIOA_CODR ((AT91_REG *) 0xFFFFF434) // (PIOA) Clear Output Data Register #define AT91C_PIOA_MDER ((AT91_REG *) 0xFFFFF450) // (PIOA) Multi-driver Enable Register #define AT91C_PIOA_PDR ((AT91_REG *) 0xFFFFF404) // (PIOA) PIO Disable Register #define AT91C_PIOA_IFSR ((AT91_REG *) 0xFFFFF428) // (PIOA) Input Filter Status Register #define AT91C_PIOA_OER ((AT91_REG *) 0xFFFFF410) // (PIOA) Output Enable Register #define AT91C_PIOA_PSR ((AT91_REG *) 0xFFFFF408) // (PIOA) PIO Status Register // ========== Register definition for CKGR peripheral ========== #define AT91C_CKGR_MOR ((AT91_REG *) 0xFFFFFC20) // (CKGR) Main Oscillator Register #define AT91C_CKGR_PLLR ((AT91_REG *) 0xFFFFFC2C) // (CKGR) PLL Register #define AT91C_CKGR_MCFR ((AT91_REG *) 0xFFFFFC24) // (CKGR) Main Clock Frequency Register // ========== Register definition for PMC peripheral ========== #define AT91C_PMC_IDR ((AT91_REG *) 0xFFFFFC64) // (PMC) Interrupt Disable Register #define AT91C_PMC_MOR ((AT91_REG *) 0xFFFFFC20) // (PMC) Main Oscillator Register #define AT91C_PMC_PLLR ((AT91_REG *) 0xFFFFFC2C) // (PMC) PLL Register #define AT91C_PMC_PCER ((AT91_REG *) 0xFFFFFC10) // (PMC) Peripheral Clock Enable Register #define AT91C_PMC_PCKR ((AT91_REG *) 0xFFFFFC40) // (PMC) Programmable Clock Register #define AT91C_PMC_MCKR ((AT91_REG *) 0xFFFFFC30) // (PMC) Master Clock Register #define AT91C_PMC_SCDR ((AT91_REG *) 0xFFFFFC04) // (PMC) System Clock Disable Register #define AT91C_PMC_PCDR ((AT91_REG *) 0xFFFFFC14) // (PMC) Peripheral Clock Disable Register #define AT91C_PMC_SCSR ((AT91_REG *) 0xFFFFFC08) // (PMC) System Clock Status Register #define AT91C_PMC_PCSR ((AT91_REG *) 0xFFFFFC18) // (PMC) Peripheral Clock Status Register #define AT91C_PMC_MCFR ((AT91_REG *) 0xFFFFFC24) // (PMC) Main Clock Frequency Register #define AT91C_PMC_SCER ((AT91_REG *) 0xFFFFFC00) // (PMC) System Clock Enable Register #define AT91C_PMC_IMR ((AT91_REG *) 0xFFFFFC6C) // (PMC) Interrupt Mask Register #define AT91C_PMC_IER ((AT91_REG *) 0xFFFFFC60) // (PMC) Interrupt Enable Register #define AT91C_PMC_SR ((AT91_REG *) 0xFFFFFC68) // (PMC) Status Register // ========== Register definition for RSTC peripheral ========== #define AT91C_RSTC_RCR ((AT91_REG *) 0xFFFFFD00) // (RSTC) Reset Control Register #define AT91C_RSTC_RMR ((AT91_REG *) 0xFFFFFD08) // (RSTC) Reset Mode Register #define AT91C_RSTC_RSR ((AT91_REG *) 0xFFFFFD04) // (RSTC) Reset Status Register // ========== Register definition for RTTC peripheral ========== #define AT91C_RTTC_RTSR ((AT91_REG *) 0xFFFFFD2C) // (RTTC) Real-time Status Register #define AT91C_RTTC_RTMR ((AT91_REG *) 0xFFFFFD20) // (RTTC) Real-time Mode Register #define AT91C_RTTC_RTVR ((AT91_REG *) 0xFFFFFD28) // (RTTC) Real-time Value Register #define AT91C_RTTC_RTAR ((AT91_REG *) 0xFFFFFD24) // (RTTC) Real-time Alarm Register // ========== Register definition for PITC peripheral ========== #define AT91C_PITC_PIVR ((AT91_REG *) 0xFFFFFD38) // (PITC) Period Interval Value Register #define AT91C_PITC_PISR ((AT91_REG *) 0xFFFFFD34) // (PITC) Period Interval Status Register #define AT91C_PITC_PIIR ((AT91_REG *) 0xFFFFFD3C) // (PITC) Period Interval Image Register #define AT91C_PITC_PIMR ((AT91_REG *) 0xFFFFFD30) // (PITC) Period Interval Mode Register // ========== Register definition for WDTC peripheral ========== #define AT91C_WDTC_WDCR ((AT91_REG *) 0xFFFFFD40) // (WDTC) Watchdog Control Register #define AT91C_WDTC_WDSR ((AT91_REG *) 0xFFFFFD48) // (WDTC) Watchdog Status Register #define AT91C_WDTC_WDMR ((AT91_REG *) 0xFFFFFD44) // (WDTC) Watchdog Mode Register // ========== Register definition for VREG peripheral ========== #define AT91C_VREG_MR ((AT91_REG *) 0xFFFFFD60) // (VREG) Voltage Regulator Mode Register // ========== Register definition for MC peripheral ========== #define AT91C_MC_ASR ((AT91_REG *) 0xFFFFFF04) // (MC) MC Abort Status Register #define AT91C_MC_RCR ((AT91_REG *) 0xFFFFFF00) // (MC) MC Remap Control Register #define AT91C_MC_FCR ((AT91_REG *) 0xFFFFFF64) // (MC) MC Flash Command Register #define AT91C_MC_AASR ((AT91_REG *) 0xFFFFFF08) // (MC) MC Abort Address Status Register #define AT91C_MC_FSR ((AT91_REG *) 0xFFFFFF68) // (MC) MC Flash Status Register #define AT91C_MC_FMR ((AT91_REG *) 0xFFFFFF60) // (MC) MC Flash Mode Register // ========== Register definition for PDC_SPI peripheral ========== #define AT91C_SPI_PTCR ((AT91_REG *) 0xFFFE0120) // (PDC_SPI) PDC Transfer Control Register #define AT91C_SPI_TPR ((AT91_REG *) 0xFFFE0108) // (PDC_SPI) Transmit Pointer Register #define AT91C_SPI_TCR ((AT91_REG *) 0xFFFE010C) // (PDC_SPI) Transmit Counter Register #define AT91C_SPI_RCR ((AT91_REG *) 0xFFFE0104) // (PDC_SPI) Receive Counter Register #define AT91C_SPI_PTSR ((AT91_REG *) 0xFFFE0124) // (PDC_SPI) PDC Transfer Status Register #define AT91C_SPI_RNPR ((AT91_REG *) 0xFFFE0110) // (PDC_SPI) Receive Next Pointer Register #define AT91C_SPI_RPR ((AT91_REG *) 0xFFFE0100) // (PDC_SPI) Receive Pointer Register #define AT91C_SPI_TNCR ((AT91_REG *) 0xFFFE011C) // (PDC_SPI) Transmit Next Counter Register #define AT91C_SPI_RNCR ((AT91_REG *) 0xFFFE0114) // (PDC_SPI) Receive Next Counter Register #define AT91C_SPI_TNPR ((AT91_REG *) 0xFFFE0118) // (PDC_SPI) Transmit Next Pointer Register // ========== Register definition for SPI peripheral ========== #define AT91C_SPI_IER ((AT91_REG *) 0xFFFE0014) // (SPI) Interrupt Enable Register #define AT91C_SPI_SR ((AT91_REG *) 0xFFFE0010) // (SPI) Status Register #define AT91C_SPI_IDR ((AT91_REG *) 0xFFFE0018) // (SPI) Interrupt Disable Register #define AT91C_SPI_CR ((AT91_REG *) 0xFFFE0000) // (SPI) Control Register #define AT91C_SPI_MR ((AT91_REG *) 0xFFFE0004) // (SPI) Mode Register #define AT91C_SPI_IMR ((AT91_REG *) 0xFFFE001C) // (SPI) Interrupt Mask Register #define AT91C_SPI_TDR ((AT91_REG *) 0xFFFE000C) // (SPI) Transmit Data Register #define AT91C_SPI_RDR ((AT91_REG *) 0xFFFE0008) // (SPI) Receive Data Register #define AT91C_SPI_CSR ((AT91_REG *) 0xFFFE0030) // (SPI) Chip Select Register // ========== Register definition for PDC_ADC peripheral ========== #define AT91C_ADC_PTSR ((AT91_REG *) 0xFFFD8124) // (PDC_ADC) PDC Transfer Status Register #define AT91C_ADC_PTCR ((AT91_REG *) 0xFFFD8120) // (PDC_ADC) PDC Transfer Control Register #define AT91C_ADC_TNPR ((AT91_REG *) 0xFFFD8118) // (PDC_ADC) Transmit Next Pointer Register #define AT91C_ADC_TNCR ((AT91_REG *) 0xFFFD811C) // (PDC_ADC) Transmit Next Counter Register #define AT91C_ADC_RNPR ((AT91_REG *) 0xFFFD8110) // (PDC_ADC) Receive Next Pointer Register #define AT91C_ADC_RNCR ((AT91_REG *) 0xFFFD8114) // (PDC_ADC) Receive Next Counter Register #define AT91C_ADC_RPR ((AT91_REG *) 0xFFFD8100) // (PDC_ADC) Receive Pointer Register #define AT91C_ADC_TCR ((AT91_REG *) 0xFFFD810C) // (PDC_ADC) Transmit Counter Register #define AT91C_ADC_TPR ((AT91_REG *) 0xFFFD8108) // (PDC_ADC) Transmit Pointer Register #define AT91C_ADC_RCR ((AT91_REG *) 0xFFFD8104) // (PDC_ADC) Receive Counter Register // ========== Register definition for ADC peripheral ========== #define AT91C_ADC_CDR2 ((AT91_REG *) 0xFFFD8038) // (ADC) ADC Channel Data Register 2 #define AT91C_ADC_CDR3 ((AT91_REG *) 0xFFFD803C) // (ADC) ADC Channel Data Register 3 #define AT91C_ADC_CDR0 ((AT91_REG *) 0xFFFD8030) // (ADC) ADC Channel Data Register 0 #define AT91C_ADC_CDR5 ((AT91_REG *) 0xFFFD8044) // (ADC) ADC Channel Data Register 5 #define AT91C_ADC_CHDR ((AT91_REG *) 0xFFFD8014) // (ADC) ADC Channel Disable Register #define AT91C_ADC_SR ((AT91_REG *) 0xFFFD801C) // (ADC) ADC Status Register #define AT91C_ADC_CDR4 ((AT91_REG *) 0xFFFD8040) // (ADC) ADC Channel Data Register 4 #define AT91C_ADC_CDR1 ((AT91_REG *) 0xFFFD8034) // (ADC) ADC Channel Data Register 1 #define AT91C_ADC_LCDR ((AT91_REG *) 0xFFFD8020) // (ADC) ADC Last Converted Data Register #define AT91C_ADC_IDR ((AT91_REG *) 0xFFFD8028) // (ADC) ADC Interrupt Disable Register #define AT91C_ADC_CR ((AT91_REG *) 0xFFFD8000) // (ADC) ADC Control Register #define AT91C_ADC_CDR7 ((AT91_REG *) 0xFFFD804C) // (ADC) ADC Channel Data Register 7 #define AT91C_ADC_CDR6 ((AT91_REG *) 0xFFFD8048) // (ADC) ADC Channel Data Register 6 #define AT91C_ADC_IER ((AT91_REG *) 0xFFFD8024) // (ADC) ADC Interrupt Enable Register #define AT91C_ADC_CHER ((AT91_REG *) 0xFFFD8010) // (ADC) ADC Channel Enable Register #define AT91C_ADC_CHSR ((AT91_REG *) 0xFFFD8018) // (ADC) ADC Channel Status Register #define AT91C_ADC_MR ((AT91_REG *) 0xFFFD8004) // (ADC) ADC Mode Register #define AT91C_ADC_IMR ((AT91_REG *) 0xFFFD802C) // (ADC) ADC Interrupt Mask Register // ========== Register definition for PDC_SSC peripheral ========== #define AT91C_SSC_TNCR ((AT91_REG *) 0xFFFD411C) // (PDC_SSC) Transmit Next Counter Register #define AT91C_SSC_RPR ((AT91_REG *) 0xFFFD4100) // (PDC_SSC) Receive Pointer Register #define AT91C_SSC_RNCR ((AT91_REG *) 0xFFFD4114) // (PDC_SSC) Receive Next Counter Register #define AT91C_SSC_TPR ((AT91_REG *) 0xFFFD4108) // (PDC_SSC) Transmit Pointer Register #define AT91C_SSC_PTCR ((AT91_REG *) 0xFFFD4120) // (PDC_SSC) PDC Transfer Control Register #define AT91C_SSC_TCR ((AT91_REG *) 0xFFFD410C) // (PDC_SSC) Transmit Counter Register #define AT91C_SSC_RCR ((AT91_REG *) 0xFFFD4104) // (PDC_SSC) Receive Counter Register #define AT91C_SSC_RNPR ((AT91_REG *) 0xFFFD4110) // (PDC_SSC) Receive Next Pointer Register #define AT91C_SSC_TNPR ((AT91_REG *) 0xFFFD4118) // (PDC_SSC) Transmit Next Pointer Register #define AT91C_SSC_PTSR ((AT91_REG *) 0xFFFD4124) // (PDC_SSC) PDC Transfer Status Register // ========== Register definition for SSC peripheral ========== #define AT91C_SSC_RHR ((AT91_REG *) 0xFFFD4020) // (SSC) Receive Holding Register #define AT91C_SSC_RSHR ((AT91_REG *) 0xFFFD4030) // (SSC) Receive Sync Holding Register #define AT91C_SSC_TFMR ((AT91_REG *) 0xFFFD401C) // (SSC) Transmit Frame Mode Register #define AT91C_SSC_IDR ((AT91_REG *) 0xFFFD4048) // (SSC) Interrupt Disable Register #define AT91C_SSC_THR ((AT91_REG *) 0xFFFD4024) // (SSC) Transmit Holding Register #define AT91C_SSC_RCMR ((AT91_REG *) 0xFFFD4010) // (SSC) Receive Clock ModeRegister #define AT91C_SSC_IER ((AT91_REG *) 0xFFFD4044) // (SSC) Interrupt Enable Register #define AT91C_SSC_TSHR ((AT91_REG *) 0xFFFD4034) // (SSC) Transmit Sync Holding Register #define AT91C_SSC_SR ((AT91_REG *) 0xFFFD4040) // (SSC) Status Register #define AT91C_SSC_CMR ((AT91_REG *) 0xFFFD4004) // (SSC) Clock Mode Register #define AT91C_SSC_TCMR ((AT91_REG *) 0xFFFD4018) // (SSC) Transmit Clock Mode Register #define AT91C_SSC_CR ((AT91_REG *) 0xFFFD4000) // (SSC) Control Register #define AT91C_SSC_IMR ((AT91_REG *) 0xFFFD404C) // (SSC) Interrupt Mask Register #define AT91C_SSC_RFMR ((AT91_REG *) 0xFFFD4014) // (SSC) Receive Frame Mode Register // ========== Register definition for PDC_US1 peripheral ========== #define AT91C_US1_RNCR ((AT91_REG *) 0xFFFC4114) // (PDC_US1) Receive Next Counter Register #define AT91C_US1_PTCR ((AT91_REG *) 0xFFFC4120) // (PDC_US1) PDC Transfer Control Register #define AT91C_US1_TCR ((AT91_REG *) 0xFFFC410C) // (PDC_US1) Transmit Counter Register #define AT91C_US1_PTSR ((AT91_REG *) 0xFFFC4124) // (PDC_US1) PDC Transfer Status Register #define AT91C_US1_TNPR ((AT91_REG *) 0xFFFC4118) // (PDC_US1) Transmit Next Pointer Register #define AT91C_US1_RCR ((AT91_REG *) 0xFFFC4104) // (PDC_US1) Receive Counter Register #define AT91C_US1_RNPR ((AT91_REG *) 0xFFFC4110) // (PDC_US1) Receive Next Pointer Register #define AT91C_US1_RPR ((AT91_REG *) 0xFFFC4100) // (PDC_US1) Receive Pointer Register #define AT91C_US1_TNCR ((AT91_REG *) 0xFFFC411C) // (PDC_US1) Transmit Next Counter Register #define AT91C_US1_TPR ((AT91_REG *) 0xFFFC4108) // (PDC_US1) Transmit Pointer Register // ========== Register definition for US1 peripheral ========== #define AT91C_US1_IF ((AT91_REG *) 0xFFFC404C) // (US1) IRDA_FILTER Register #define AT91C_US1_NER ((AT91_REG *) 0xFFFC4044) // (US1) Nb Errors Register #define AT91C_US1_RTOR ((AT91_REG *) 0xFFFC4024) // (US1) Receiver Time-out Register #define AT91C_US1_CSR ((AT91_REG *) 0xFFFC4014) // (US1) Channel Status Register #define AT91C_US1_IDR ((AT91_REG *) 0xFFFC400C) // (US1) Interrupt Disable Register #define AT91C_US1_IER ((AT91_REG *) 0xFFFC4008) // (US1) Interrupt Enable Register #define AT91C_US1_THR ((AT91_REG *) 0xFFFC401C) // (US1) Transmitter Holding Register #define AT91C_US1_TTGR ((AT91_REG *) 0xFFFC4028) // (US1) Transmitter Time-guard Register #define AT91C_US1_RHR ((AT91_REG *) 0xFFFC4018) // (US1) Receiver Holding Register #define AT91C_US1_BRGR ((AT91_REG *) 0xFFFC4020) // (US1) Baud Rate Generator Register #define AT91C_US1_IMR ((AT91_REG *) 0xFFFC4010) // (US1) Interrupt Mask Register #define AT91C_US1_FIDI ((AT91_REG *) 0xFFFC4040) // (US1) FI_DI_Ratio Register #define AT91C_US1_CR ((AT91_REG *) 0xFFFC4000) // (US1) Control Register #define AT91C_US1_MR ((AT91_REG *) 0xFFFC4004) // (US1) Mode Register // ========== Register definition for PDC_US0 peripheral ========== #define AT91C_US0_TNPR ((AT91_REG *) 0xFFFC0118) // (PDC_US0) Transmit Next Pointer Register #define AT91C_US0_RNPR ((AT91_REG *) 0xFFFC0110) // (PDC_US0) Receive Next Pointer Register #define AT91C_US0_TCR ((AT91_REG *) 0xFFFC010C) // (PDC_US0) Transmit Counter Register #define AT91C_US0_PTCR ((AT91_REG *) 0xFFFC0120) // (PDC_US0) PDC Transfer Control Register #define AT91C_US0_PTSR ((AT91_REG *) 0xFFFC0124) // (PDC_US0) PDC Transfer Status Register #define AT91C_US0_TNCR ((AT91_REG *) 0xFFFC011C) // (PDC_US0) Transmit Next Counter Register #define AT91C_US0_TPR ((AT91_REG *) 0xFFFC0108) // (PDC_US0) Transmit Pointer Register #define AT91C_US0_RCR ((AT91_REG *) 0xFFFC0104) // (PDC_US0) Receive Counter Register #define AT91C_US0_RPR ((AT91_REG *) 0xFFFC0100) // (PDC_US0) Receive Pointer Register #define AT91C_US0_RNCR ((AT91_REG *) 0xFFFC0114) // (PDC_US0) Receive Next Counter Register // ========== Register definition for US0 peripheral ========== #define AT91C_US0_BRGR ((AT91_REG *) 0xFFFC0020) // (US0) Baud Rate Generator Register #define AT91C_US0_NER ((AT91_REG *) 0xFFFC0044) // (US0) Nb Errors Register #define AT91C_US0_CR ((AT91_REG *) 0xFFFC0000) // (US0) Control Register #define AT91C_US0_IMR ((AT91_REG *) 0xFFFC0010) // (US0) Interrupt Mask Register #define AT91C_US0_FIDI ((AT91_REG *) 0xFFFC0040) // (US0) FI_DI_Ratio Register #define AT91C_US0_TTGR ((AT91_REG *) 0xFFFC0028) // (US0) Transmitter Time-guard Register #define AT91C_US0_MR ((AT91_REG *) 0xFFFC0004) // (US0) Mode Register #define AT91C_US0_RTOR ((AT91_REG *) 0xFFFC0024) // (US0) Receiver Time-out Register #define AT91C_US0_CSR ((AT91_REG *) 0xFFFC0014) // (US0) Channel Status Register #define AT91C_US0_RHR ((AT91_REG *) 0xFFFC0018) // (US0) Receiver Holding Register #define AT91C_US0_IDR ((AT91_REG *) 0xFFFC000C) // (US0) Interrupt Disable Register #define AT91C_US0_THR ((AT91_REG *) 0xFFFC001C) // (US0) Transmitter Holding Register #define AT91C_US0_IF ((AT91_REG *) 0xFFFC004C) // (US0) IRDA_FILTER Register #define AT91C_US0_IER ((AT91_REG *) 0xFFFC0008) // (US0) Interrupt Enable Register // ========== Register definition for TWI peripheral ========== #define AT91C_TWI_IER ((AT91_REG *) 0xFFFB8024) // (TWI) Interrupt Enable Register #define AT91C_TWI_CR ((AT91_REG *) 0xFFFB8000) // (TWI) Control Register #define AT91C_TWI_SR ((AT91_REG *) 0xFFFB8020) // (TWI) Status Register #define AT91C_TWI_IMR ((AT91_REG *) 0xFFFB802C) // (TWI) Interrupt Mask Register #define AT91C_TWI_THR ((AT91_REG *) 0xFFFB8034) // (TWI) Transmit Holding Register #define AT91C_TWI_IDR ((AT91_REG *) 0xFFFB8028) // (TWI) Interrupt Disable Register #define AT91C_TWI_IADR ((AT91_REG *) 0xFFFB800C) // (TWI) Internal Address Register #define AT91C_TWI_MMR ((AT91_REG *) 0xFFFB8004) // (TWI) Master Mode Register #define AT91C_TWI_CWGR ((AT91_REG *) 0xFFFB8010) // (TWI) Clock Waveform Generator Register #define AT91C_TWI_RHR ((AT91_REG *) 0xFFFB8030) // (TWI) Receive Holding Register // ========== Register definition for TC0 peripheral ========== #define AT91C_TC0_SR ((AT91_REG *) 0xFFFA0020) // (TC0) Status Register #define AT91C_TC0_RC ((AT91_REG *) 0xFFFA001C) // (TC0) Register C #define AT91C_TC0_RB ((AT91_REG *) 0xFFFA0018) // (TC0) Register B #define AT91C_TC0_CCR ((AT91_REG *) 0xFFFA0000) // (TC0) Channel Control Register #define AT91C_TC0_CMR ((AT91_REG *) 0xFFFA0004) // (TC0) Channel Mode Register (Capture Mode / Waveform Mode) #define AT91C_TC0_IER ((AT91_REG *) 0xFFFA0024) // (TC0) Interrupt Enable Register #define AT91C_TC0_RA ((AT91_REG *) 0xFFFA0014) // (TC0) Register A #define AT91C_TC0_IDR ((AT91_REG *) 0xFFFA0028) // (TC0) Interrupt Disable Register #define AT91C_TC0_CV ((AT91_REG *) 0xFFFA0010) // (TC0) Counter Value #define AT91C_TC0_IMR ((AT91_REG *) 0xFFFA002C) // (TC0) Interrupt Mask Register // ========== Register definition for TC1 peripheral ========== #define AT91C_TC1_RB ((AT91_REG *) 0xFFFA0058) // (TC1) Register B #define AT91C_TC1_CCR ((AT91_REG *) 0xFFFA0040) // (TC1) Channel Control Register #define AT91C_TC1_IER ((AT91_REG *) 0xFFFA0064) // (TC1) Interrupt Enable Register #define AT91C_TC1_IDR ((AT91_REG *) 0xFFFA0068) // (TC1) Interrupt Disable Register #define AT91C_TC1_SR ((AT91_REG *) 0xFFFA0060) // (TC1) Status Register #define AT91C_TC1_CMR ((AT91_REG *) 0xFFFA0044) // (TC1) Channel Mode Register (Capture Mode / Waveform Mode) #define AT91C_TC1_RA ((AT91_REG *) 0xFFFA0054) // (TC1) Register A #define AT91C_TC1_RC ((AT91_REG *) 0xFFFA005C) // (TC1) Register C #define AT91C_TC1_IMR ((AT91_REG *) 0xFFFA006C) // (TC1) Interrupt Mask Register #define AT91C_TC1_CV ((AT91_REG *) 0xFFFA0050) // (TC1) Counter Value // ========== Register definition for TC2 peripheral ========== #define AT91C_TC2_CMR ((AT91_REG *) 0xFFFA0084) // (TC2) Channel Mode Register (Capture Mode / Waveform Mode) #define AT91C_TC2_CCR ((AT91_REG *) 0xFFFA0080) // (TC2) Channel Control Register #define AT91C_TC2_CV ((AT91_REG *) 0xFFFA0090) // (TC2) Counter Value #define AT91C_TC2_RA ((AT91_REG *) 0xFFFA0094) // (TC2) Register A #define AT91C_TC2_RB ((AT91_REG *) 0xFFFA0098) // (TC2) Register B #define AT91C_TC2_IDR ((AT91_REG *) 0xFFFA00A8) // (TC2) Interrupt Disable Register #define AT91C_TC2_IMR ((AT91_REG *) 0xFFFA00AC) // (TC2) Interrupt Mask Register #define AT91C_TC2_RC ((AT91_REG *) 0xFFFA009C) // (TC2) Register C #define AT91C_TC2_IER ((AT91_REG *) 0xFFFA00A4) // (TC2) Interrupt Enable Register #define AT91C_TC2_SR ((AT91_REG *) 0xFFFA00A0) // (TC2) Status Register // ========== Register definition for TCB peripheral ========== #define AT91C_TCB_BMR ((AT91_REG *) 0xFFFA00C4) // (TCB) TC Block Mode Register #define AT91C_TCB_BCR ((AT91_REG *) 0xFFFA00C0) // (TCB) TC Block Control Register // ========== Register definition for PWMC_CH3 peripheral ========== #define AT91C_PWMC_CH3_CUPDR ((AT91_REG *) 0xFFFCC270) // (PWMC_CH3) Channel Update Register #define AT91C_PWMC_CH3_Reserved ((AT91_REG *) 0xFFFCC274) // (PWMC_CH3) Reserved #define AT91C_PWMC_CH3_CPRDR ((AT91_REG *) 0xFFFCC268) // (PWMC_CH3) Channel Period Register #define AT91C_PWMC_CH3_CDTYR ((AT91_REG *) 0xFFFCC264) // (PWMC_CH3) Channel Duty Cycle Register #define AT91C_PWMC_CH3_CCNTR ((AT91_REG *) 0xFFFCC26C) // (PWMC_CH3) Channel Counter Register #define AT91C_PWMC_CH3_CMR ((AT91_REG *) 0xFFFCC260) // (PWMC_CH3) Channel Mode Register // ========== Register definition for PWMC_CH2 peripheral ========== #define AT91C_PWMC_CH2_Reserved ((AT91_REG *) 0xFFFCC254) // (PWMC_CH2) Reserved #define AT91C_PWMC_CH2_CMR ((AT91_REG *) 0xFFFCC240) // (PWMC_CH2) Channel Mode Register #define AT91C_PWMC_CH2_CCNTR ((AT91_REG *) 0xFFFCC24C) // (PWMC_CH2) Channel Counter Register #define AT91C_PWMC_CH2_CPRDR ((AT91_REG *) 0xFFFCC248) // (PWMC_CH2) Channel Period Register #define AT91C_PWMC_CH2_CUPDR ((AT91_REG *) 0xFFFCC250) // (PWMC_CH2) Channel Update Register #define AT91C_PWMC_CH2_CDTYR ((AT91_REG *) 0xFFFCC244) // (PWMC_CH2) Channel Duty Cycle Register // ========== Register definition for PWMC_CH1 peripheral ========== #define AT91C_PWMC_CH1_Reserved ((AT91_REG *) 0xFFFCC234) // (PWMC_CH1) Reserved #define AT91C_PWMC_CH1_CUPDR ((AT91_REG *) 0xFFFCC230) // (PWMC_CH1) Channel Update Register #define AT91C_PWMC_CH1_CPRDR ((AT91_REG *) 0xFFFCC228) // (PWMC_CH1) Channel Period Register #define AT91C_PWMC_CH1_CCNTR ((AT91_REG *) 0xFFFCC22C) // (PWMC_CH1) Channel Counter Register #define AT91C_PWMC_CH1_CDTYR ((AT91_REG *) 0xFFFCC224) // (PWMC_CH1) Channel Duty Cycle Register #define AT91C_PWMC_CH1_CMR ((AT91_REG *) 0xFFFCC220) // (PWMC_CH1) Channel Mode Register // ========== Register definition for PWMC_CH0 peripheral ========== #define AT91C_PWMC_CH0_Reserved ((AT91_REG *) 0xFFFCC214) // (PWMC_CH0) Reserved #define AT91C_PWMC_CH0_CPRDR ((AT91_REG *) 0xFFFCC208) // (PWMC_CH0) Channel Period Register #define AT91C_PWMC_CH0_CDTYR ((AT91_REG *) 0xFFFCC204) // (PWMC_CH0) Channel Duty Cycle Register #define AT91C_PWMC_CH0_CMR ((AT91_REG *) 0xFFFCC200) // (PWMC_CH0) Channel Mode Register #define AT91C_PWMC_CH0_CUPDR ((AT91_REG *) 0xFFFCC210) // (PWMC_CH0) Channel Update Register #define AT91C_PWMC_CH0_CCNTR ((AT91_REG *) 0xFFFCC20C) // (PWMC_CH0) Channel Counter Register // ========== Register definition for PWMC peripheral ========== #define AT91C_PWMC_IDR ((AT91_REG *) 0xFFFCC014) // (PWMC) PWMC Interrupt Disable Register #define AT91C_PWMC_DIS ((AT91_REG *) 0xFFFCC008) // (PWMC) PWMC Disable Register #define AT91C_PWMC_IER ((AT91_REG *) 0xFFFCC010) // (PWMC) PWMC Interrupt Enable Register #define AT91C_PWMC_VR ((AT91_REG *) 0xFFFCC0FC) // (PWMC) PWMC Version Register #define AT91C_PWMC_ISR ((AT91_REG *) 0xFFFCC01C) // (PWMC) PWMC Interrupt Status Register #define AT91C_PWMC_SR ((AT91_REG *) 0xFFFCC00C) // (PWMC) PWMC Status Register #define AT91C_PWMC_IMR ((AT91_REG *) 0xFFFCC018) // (PWMC) PWMC Interrupt Mask Register #define AT91C_PWMC_MR ((AT91_REG *) 0xFFFCC000) // (PWMC) PWMC Mode Register #define AT91C_PWMC_ENA ((AT91_REG *) 0xFFFCC004) // (PWMC) PWMC Enable Register // ========== Register definition for UDP peripheral ========== #define AT91C_UDP_IMR ((AT91_REG *) 0xFFFB0018) // (UDP) Interrupt Mask Register #define AT91C_UDP_FADDR ((AT91_REG *) 0xFFFB0008) // (UDP) Function Address Register #define AT91C_UDP_NUM ((AT91_REG *) 0xFFFB0000) // (UDP) Frame Number Register #define AT91C_UDP_FDR ((AT91_REG *) 0xFFFB0050) // (UDP) Endpoint FIFO Data Register #define AT91C_UDP_ISR ((AT91_REG *) 0xFFFB001C) // (UDP) Interrupt Status Register #define AT91C_UDP_CSR ((AT91_REG *) 0xFFFB0030) // (UDP) Endpoint Control and Status Register #define AT91C_UDP_IDR ((AT91_REG *) 0xFFFB0014) // (UDP) Interrupt Disable Register #define AT91C_UDP_ICR ((AT91_REG *) 0xFFFB0020) // (UDP) Interrupt Clear Register #define AT91C_UDP_RSTEP ((AT91_REG *) 0xFFFB0028) // (UDP) Reset Endpoint Register #define AT91C_UDP_TXVC ((AT91_REG *) 0xFFFB0074) // (UDP) Transceiver Control Register #define AT91C_UDP_GLBSTATE ((AT91_REG *) 0xFFFB0004) // (UDP) Global State Register #define AT91C_UDP_IER ((AT91_REG *) 0xFFFB0010) // (UDP) Interrupt Enable Register // ***************************************************************************** // PIO DEFINITIONS FOR AT91SAM7S256 // ***************************************************************************** #define AT91C_PIO_PA0 ((unsigned int) 1 << 0) // Pin Controlled by PA0 #define AT91C_PA0_PWM0 ((unsigned int) AT91C_PIO_PA0) // PWM Channel 0 #define AT91C_PA0_TIOA0 ((unsigned int) AT91C_PIO_PA0) // Timer Counter 0 Multipurpose Timer I/O Pin A #define AT91C_PIO_PA1 ((unsigned int) 1 << 1) // Pin Controlled by PA1 #define AT91C_PA1_PWM1 ((unsigned int) AT91C_PIO_PA1) // PWM Channel 1 #define AT91C_PA1_TIOB0 ((unsigned int) AT91C_PIO_PA1) // Timer Counter 0 Multipurpose Timer I/O Pin B #define AT91C_PIO_PA10 ((unsigned int) 1 << 10) // Pin Controlled by PA10 #define AT91C_PA10_DTXD ((unsigned int) AT91C_PIO_PA10) // DBGU Debug Transmit Data #define AT91C_PA10_NPCS2 ((unsigned int) AT91C_PIO_PA10) // SPI Peripheral Chip Select 2 #define AT91C_PIO_PA11 ((unsigned int) 1 << 11) // Pin Controlled by PA11 #define AT91C_PA11_NPCS0 ((unsigned int) AT91C_PIO_PA11) // SPI Peripheral Chip Select 0 #define AT91C_PA11_PWM0 ((unsigned int) AT91C_PIO_PA11) // PWM Channel 0 #define AT91C_PIO_PA12 ((unsigned int) 1 << 12) // Pin Controlled by PA12 #define AT91C_PA12_MISO ((unsigned int) AT91C_PIO_PA12) // SPI Master In Slave #define AT91C_PA12_PWM1 ((unsigned int) AT91C_PIO_PA12) // PWM Channel 1 #define AT91C_PIO_PA13 ((unsigned int) 1 << 13) // Pin Controlled by PA13 #define AT91C_PA13_MOSI ((unsigned int) AT91C_PIO_PA13) // SPI Master Out Slave #define AT91C_PA13_PWM2 ((unsigned int) AT91C_PIO_PA13) // PWM Channel 2 #define AT91C_PIO_PA14 ((unsigned int) 1 << 14) // Pin Controlled by PA14 #define AT91C_PA14_SPCK ((unsigned int) AT91C_PIO_PA14) // SPI Serial Clock #define AT91C_PA14_PWM3 ((unsigned int) AT91C_PIO_PA14) // PWM Channel 3 #define AT91C_PIO_PA15 ((unsigned int) 1 << 15) // Pin Controlled by PA15 #define AT91C_PA15_TF ((unsigned int) AT91C_PIO_PA15) // SSC Transmit Frame Sync #define AT91C_PA15_TIOA1 ((unsigned int) AT91C_PIO_PA15) // Timer Counter 1 Multipurpose Timer I/O Pin A #define AT91C_PIO_PA16 ((unsigned int) 1 << 16) // Pin Controlled by PA16 #define AT91C_PA16_TK ((unsigned int) AT91C_PIO_PA16) // SSC Transmit Clock #define AT91C_PA16_TIOB1 ((unsigned int) AT91C_PIO_PA16) // Timer Counter 1 Multipurpose Timer I/O Pin B #define AT91C_PIO_PA17 ((unsigned int) 1 << 17) // Pin Controlled by PA17 #define AT91C_PA17_TD ((unsigned int) AT91C_PIO_PA17) // SSC Transmit data #define AT91C_PA17_PCK1 ((unsigned int) AT91C_PIO_PA17) // PMC Programmable Clock Output 1 #define AT91C_PIO_PA18 ((unsigned int) 1 << 18) // Pin Controlled by PA18 #define AT91C_PA18_RD ((unsigned int) AT91C_PIO_PA18) // SSC Receive Data #define AT91C_PA18_PCK2 ((unsigned int) AT91C_PIO_PA18) // PMC Programmable Clock Output 2 #define AT91C_PIO_PA19 ((unsigned int) 1 << 19) // Pin Controlled by PA19 #define AT91C_PA19_RK ((unsigned int) AT91C_PIO_PA19) // SSC Receive Clock #define AT91C_PA19_FIQ ((unsigned int) AT91C_PIO_PA19) // AIC Fast Interrupt Input #define AT91C_PIO_PA2 ((unsigned int) 1 << 2) // Pin Controlled by PA2 #define AT91C_PA2_PWM2 ((unsigned int) AT91C_PIO_PA2) // PWM Channel 2 #define AT91C_PA2_SCK0 ((unsigned int) AT91C_PIO_PA2) // USART 0 Serial Clock #define AT91C_PIO_PA20 ((unsigned int) 1 << 20) // Pin Controlled by PA20 #define AT91C_PA20_RF ((unsigned int) AT91C_PIO_PA20) // SSC Receive Frame Sync #define AT91C_PA20_IRQ0 ((unsigned int) AT91C_PIO_PA20) // External Interrupt 0 #define AT91C_PIO_PA21 ((unsigned int) 1 << 21) // Pin Controlled by PA21 #define AT91C_PA21_RXD1 ((unsigned int) AT91C_PIO_PA21) // USART 1 Receive Data #define AT91C_PA21_PCK1 ((unsigned int) AT91C_PIO_PA21) // PMC Programmable Clock Output 1 #define AT91C_PIO_PA22 ((unsigned int) 1 << 22) // Pin Controlled by PA22 #define AT91C_PA22_TXD1 ((unsigned int) AT91C_PIO_PA22) // USART 1 Transmit Data #define AT91C_PA22_NPCS3 ((unsigned int) AT91C_PIO_PA22) // SPI Peripheral Chip Select 3 #define AT91C_PIO_PA23 ((unsigned int) 1 << 23) // Pin Controlled by PA23 #define AT91C_PA23_SCK1 ((unsigned int) AT91C_PIO_PA23) // USART 1 Serial Clock #define AT91C_PA23_PWM0 ((unsigned int) AT91C_PIO_PA23) // PWM Channel 0 #define AT91C_PIO_PA24 ((unsigned int) 1 << 24) // Pin Controlled by PA24 #define AT91C_PA24_RTS1 ((unsigned int) AT91C_PIO_PA24) // USART 1 Ready To Send #define AT91C_PA24_PWM1 ((unsigned int) AT91C_PIO_PA24) // PWM Channel 1 #define AT91C_PIO_PA25 ((unsigned int) 1 << 25) // Pin Controlled by PA25 #define AT91C_PA25_CTS1 ((unsigned int) AT91C_PIO_PA25) // USART 1 Clear To Send #define AT91C_PA25_PWM2 ((unsigned int) AT91C_PIO_PA25) // PWM Channel 2 #define AT91C_PIO_PA26 ((unsigned int) 1 << 26) // Pin Controlled by PA26 #define AT91C_PA26_DCD1 ((unsigned int) AT91C_PIO_PA26) // USART 1 Data Carrier Detect #define AT91C_PA26_TIOA2 ((unsigned int) AT91C_PIO_PA26) // Timer Counter 2 Multipurpose Timer I/O Pin A #define AT91C_PIO_PA27 ((unsigned int) 1 << 27) // Pin Controlled by PA27 #define AT91C_PA27_DTR1 ((unsigned int) AT91C_PIO_PA27) // USART 1 Data Terminal ready #define AT91C_PA27_TIOB2 ((unsigned int) AT91C_PIO_PA27) // Timer Counter 2 Multipurpose Timer I/O Pin B #define AT91C_PIO_PA28 ((unsigned int) 1 << 28) // Pin Controlled by PA28 #define AT91C_PA28_DSR1 ((unsigned int) AT91C_PIO_PA28) // USART 1 Data Set ready #define AT91C_PA28_TCLK1 ((unsigned int) AT91C_PIO_PA28) // Timer Counter 1 external clock input #define AT91C_PIO_PA29 ((unsigned int) 1 << 29) // Pin Controlled by PA29 #define AT91C_PA29_RI1 ((unsigned int) AT91C_PIO_PA29) // USART 1 Ring Indicator #define AT91C_PA29_TCLK2 ((unsigned int) AT91C_PIO_PA29) // Timer Counter 2 external clock input #define AT91C_PIO_PA3 ((unsigned int) 1 << 3) // Pin Controlled by PA3 #define AT91C_PA3_TWD ((unsigned int) AT91C_PIO_PA3) // TWI Two-wire Serial Data #define AT91C_PA3_NPCS3 ((unsigned int) AT91C_PIO_PA3) // SPI Peripheral Chip Select 3 #define AT91C_PIO_PA30 ((unsigned int) 1 << 30) // Pin Controlled by PA30 #define AT91C_PA30_IRQ1 ((unsigned int) AT91C_PIO_PA30) // External Interrupt 1 #define AT91C_PA30_NPCS2 ((unsigned int) AT91C_PIO_PA30) // SPI Peripheral Chip Select 2 #define AT91C_PIO_PA31 ((unsigned int) 1 << 31) // Pin Controlled by PA31 #define AT91C_PA31_NPCS1 ((unsigned int) AT91C_PIO_PA31) // SPI Peripheral Chip Select 1 #define AT91C_PA31_PCK2 ((unsigned int) AT91C_PIO_PA31) // PMC Programmable Clock Output 2 #define AT91C_PIO_PA4 ((unsigned int) 1 << 4) // Pin Controlled by PA4 #define AT91C_PA4_TWCK ((unsigned int) AT91C_PIO_PA4) // TWI Two-wire Serial Clock #define AT91C_PA4_TCLK0 ((unsigned int) AT91C_PIO_PA4) // Timer Counter 0 external clock input #define AT91C_PIO_PA5 ((unsigned int) 1 << 5) // Pin Controlled by PA5 #define AT91C_PA5_RXD0 ((unsigned int) AT91C_PIO_PA5) // USART 0 Receive Data #define AT91C_PA5_NPCS3 ((unsigned int) AT91C_PIO_PA5) // SPI Peripheral Chip Select 3 #define AT91C_PIO_PA6 ((unsigned int) 1 << 6) // Pin Controlled by PA6 #define AT91C_PA6_TXD0 ((unsigned int) AT91C_PIO_PA6) // USART 0 Transmit Data #define AT91C_PA6_PCK0 ((unsigned int) AT91C_PIO_PA6) // PMC Programmable Clock Output 0 #define AT91C_PIO_PA7 ((unsigned int) 1 << 7) // Pin Controlled by PA7 #define AT91C_PA7_RTS0 ((unsigned int) AT91C_PIO_PA7) // USART 0 Ready To Send #define AT91C_PA7_PWM3 ((unsigned int) AT91C_PIO_PA7) // PWM Channel 3 #define AT91C_PIO_PA8 ((unsigned int) 1 << 8) // Pin Controlled by PA8 #define AT91C_PA8_CTS0 ((unsigned int) AT91C_PIO_PA8) // USART 0 Clear To Send #define AT91C_PA8_ADTRG ((unsigned int) AT91C_PIO_PA8) // ADC External Trigger #define AT91C_PIO_PA9 ((unsigned int) 1 << 9) // Pin Controlled by PA9 #define AT91C_PA9_DRXD ((unsigned int) AT91C_PIO_PA9) // DBGU Debug Receive Data #define AT91C_PA9_NPCS1 ((unsigned int) AT91C_PIO_PA9) // SPI Peripheral Chip Select 1 // ***************************************************************************** // PERIPHERAL ID DEFINITIONS FOR AT91SAM7S256 // ***************************************************************************** #define AT91C_ID_FIQ ((unsigned int) 0) // Advanced Interrupt Controller (FIQ) #define AT91C_ID_SYS ((unsigned int) 1) // System Peripheral #define AT91C_ID_PIOA ((unsigned int) 2) // Parallel IO Controller #define AT91C_ID_3_Reserved ((unsigned int) 3) // Reserved #define AT91C_ID_ADC ((unsigned int) 4) // Analog-to-Digital Converter #define AT91C_ID_SPI ((unsigned int) 5) // Serial Peripheral Interface #define AT91C_ID_US0 ((unsigned int) 6) // USART 0 #define AT91C_ID_US1 ((unsigned int) 7) // USART 1 #define AT91C_ID_SSC ((unsigned int) 8) // Serial Synchronous Controller #define AT91C_ID_TWI ((unsigned int) 9) // Two-Wire Interface #define AT91C_ID_PWMC ((unsigned int) 10) // PWM Controller #define AT91C_ID_UDP ((unsigned int) 11) // USB Device Port #define AT91C_ID_TC0 ((unsigned int) 12) // Timer Counter 0 #define AT91C_ID_TC1 ((unsigned int) 13) // Timer Counter 1 #define AT91C_ID_TC2 ((unsigned int) 14) // Timer Counter 2 #define AT91C_ID_15_Reserved ((unsigned int) 15) // Reserved #define AT91C_ID_16_Reserved ((unsigned int) 16) // Reserved #define AT91C_ID_17_Reserved ((unsigned int) 17) // Reserved #define AT91C_ID_18_Reserved ((unsigned int) 18) // Reserved #define AT91C_ID_19_Reserved ((unsigned int) 19) // Reserved #define AT91C_ID_20_Reserved ((unsigned int) 20) // Reserved #define AT91C_ID_21_Reserved ((unsigned int) 21) // Reserved #define AT91C_ID_22_Reserved ((unsigned int) 22) // Reserved #define AT91C_ID_23_Reserved ((unsigned int) 23) // Reserved #define AT91C_ID_24_Reserved ((unsigned int) 24) // Reserved #define AT91C_ID_25_Reserved ((unsigned int) 25) // Reserved #define AT91C_ID_26_Reserved ((unsigned int) 26) // Reserved #define AT91C_ID_27_Reserved ((unsigned int) 27) // Reserved #define AT91C_ID_28_Reserved ((unsigned int) 28) // Reserved #define AT91C_ID_29_Reserved ((unsigned int) 29) // Reserved #define AT91C_ID_IRQ0 ((unsigned int) 30) // Advanced Interrupt Controller (IRQ0) #define AT91C_ID_IRQ1 ((unsigned int) 31) // Advanced Interrupt Controller (IRQ1) // ***************************************************************************** // BASE ADDRESS DEFINITIONS FOR AT91SAM7S256 // ***************************************************************************** #define AT91C_BASE_SYS ((AT91PS_SYS) 0xFFFFF000) // (SYS) Base Address #define AT91C_BASE_AIC ((AT91PS_AIC) 0xFFFFF000) // (AIC) Base Address #define AT91C_BASE_PDC_DBGU ((AT91PS_PDC) 0xFFFFF300) // (PDC_DBGU) Base Address #define AT91C_BASE_DBGU ((AT91PS_DBGU) 0xFFFFF200) // (DBGU) Base Address #define AT91C_BASE_PIOA ((AT91PS_PIO) 0xFFFFF400) // (PIOA) Base Address #define AT91C_BASE_CKGR ((AT91PS_CKGR) 0xFFFFFC20) // (CKGR) Base Address #define AT91C_BASE_PMC ((AT91PS_PMC) 0xFFFFFC00) // (PMC) Base Address #define AT91C_BASE_RSTC ((AT91PS_RSTC) 0xFFFFFD00) // (RSTC) Base Address #define AT91C_BASE_RTTC ((AT91PS_RTTC) 0xFFFFFD20) // (RTTC) Base Address #define AT91C_BASE_PITC ((AT91PS_PITC) 0xFFFFFD30) // (PITC) Base Address #define AT91C_BASE_WDTC ((AT91PS_WDTC) 0xFFFFFD40) // (WDTC) Base Address #define AT91C_BASE_VREG ((AT91PS_VREG) 0xFFFFFD60) // (VREG) Base Address #define AT91C_BASE_MC ((AT91PS_MC) 0xFFFFFF00) // (MC) Base Address #define AT91C_BASE_PDC_SPI ((AT91PS_PDC) 0xFFFE0100) // (PDC_SPI) Base Address #define AT91C_BASE_SPI ((AT91PS_SPI) 0xFFFE0000) // (SPI) Base Address #define AT91C_BASE_PDC_ADC ((AT91PS_PDC) 0xFFFD8100) // (PDC_ADC) Base Address #define AT91C_BASE_ADC ((AT91PS_ADC) 0xFFFD8000) // (ADC) Base Address #define AT91C_BASE_PDC_SSC ((AT91PS_PDC) 0xFFFD4100) // (PDC_SSC) Base Address #define AT91C_BASE_SSC ((AT91PS_SSC) 0xFFFD4000) // (SSC) Base Address #define AT91C_BASE_PDC_US1 ((AT91PS_PDC) 0xFFFC4100) // (PDC_US1) Base Address #define AT91C_BASE_US1 ((AT91PS_USART) 0xFFFC4000) // (US1) Base Address #define AT91C_BASE_PDC_US0 ((AT91PS_PDC) 0xFFFC0100) // (PDC_US0) Base Address #define AT91C_BASE_US0 ((AT91PS_USART) 0xFFFC0000) // (US0) Base Address #define AT91C_BASE_TWI ((AT91PS_TWI) 0xFFFB8000) // (TWI) Base Address #define AT91C_BASE_TC0 ((AT91PS_TC) 0xFFFA0000) // (TC0) Base Address #define AT91C_BASE_TC1 ((AT91PS_TC) 0xFFFA0040) // (TC1) Base Address #define AT91C_BASE_TC2 ((AT91PS_TC) 0xFFFA0080) // (TC2) Base Address #define AT91C_BASE_TCB ((AT91PS_TCB) 0xFFFA0000) // (TCB) Base Address #define AT91C_BASE_PWMC_CH3 ((AT91PS_PWMC_CH) 0xFFFCC260) // (PWMC_CH3) Base Address #define AT91C_BASE_PWMC_CH2 ((AT91PS_PWMC_CH) 0xFFFCC240) // (PWMC_CH2) Base Address #define AT91C_BASE_PWMC_CH1 ((AT91PS_PWMC_CH) 0xFFFCC220) // (PWMC_CH1) Base Address #define AT91C_BASE_PWMC_CH0 ((AT91PS_PWMC_CH) 0xFFFCC200) // (PWMC_CH0) Base Address #define AT91C_BASE_PWMC ((AT91PS_PWMC) 0xFFFCC000) // (PWMC) Base Address #define AT91C_BASE_UDP ((AT91PS_UDP) 0xFFFB0000) // (UDP) Base Address // ***************************************************************************** // MEMORY MAPPING DEFINITIONS FOR AT91SAM7S256 // ***************************************************************************** #define AT91C_ISRAM ((char *) 0x00200000) // Internal SRAM base address #define AT91C_ISRAM_SIZE ((unsigned int) 0x00010000) // Internal SRAM size in byte (64 Kbyte) #define AT91C_IFLASH ((char *) 0x00100000) // Internal ROM base address #define AT91C_IFLASH_SIZE ((unsigned int) 0x00040000) // Internal ROM size in byte (256 Kbyte) #endif nxt-firmware-1.29.7/lib/000077500000000000000000000000001466344546000150175ustar00rootroot00000000000000nxt-firmware-1.29.7/lib/nxt.ld000066400000000000000000000073351466344546000161610ustar00rootroot00000000000000 MEMORY { CODE (rx) : ORIGIN = 0x00100000, LENGTH = 256k DATA (rwx) : ORIGIN = 0x00200000, LENGTH = 64k } __FIRST_IN_RAM = ORIGIN(DATA); __TOP_STACK = ORIGIN(DATA) + LENGTH(DATA); /* Section Definitions */ SECTIONS { /* first section is .text which is used for code */ . = ORIGIN(CODE); .text : { KEEP(*(.vectorg)) . = ALIGN(4); KEEP(*(.init)) *(.text .text.*) /* remaining code */ *(.gnu.linkonce.t.*) *(.glue_7) *(.glue_7t) *(.gcc_except_table) *(.rodata) /* read-only data (constants) */ *(.rodata.*) *(.gnu.linkonce.r.*) . = ALIGN(4); } >CODE . = ALIGN(4); /* .ctors .dtors are used for c++ constructors/destructors */ .ctors : { PROVIDE(__ctors_start__ = .); KEEP(*(SORT(.ctors.*))) KEEP(*(.ctors)) PROVIDE(__ctors_end__ = .); } >CODE .dtors : { PROVIDE(__dtors_start__ = .); KEEP(*(SORT(.dtors.*))) KEEP(*(.dtors)) PROVIDE(__dtors_end__ = .); } >CODE __exidx_start = . ; .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >CODE __exidx_end = . ; . = ALIGN(4); _etext = . ; PROVIDE (etext = .); /* .data section which is used for initialized data */ .data : AT (_etext) { _data = . ; KEEP(*(.vectmapped)) . = ALIGN(4); *(.fastrun .fastrun.*) . = ALIGN(4); SORT(CONSTRUCTORS) . = ALIGN(4); *(.data) *(.data.*) *(.gnu.linkonce.d.*) . = ALIGN(4); } >DATA . = ALIGN(4); _edata = . ; PROVIDE (edata = .); __STARTOFUSERFLASH_FROM_LINKER = ALIGN (LOADADDR (.data) + SIZEOF (.data), 0x100); /* * The various debugger stacks. */ .stack : ALIGN(8) { /* abort stack */ __abort_stack_bottom__ = . ; KEEP(*(.stack.abort)) __abort_stack__ = . ; __abort_stack_top__ = . ; /* debugger state */ __debugger_stack_bottom__ = . ; KEEP(*(.stack.debugger)) __debugger_stack__ = . ; __debugger_stack_top__ = . ; /* breakpoints */ __breakpoints_start__ = . ; KEEP(*(.breakpoints)) __breakpoints_end__ = . ; } > DATA __breakpoints_num__ = (__breakpoints_end__ - __breakpoints_start__) / 8; /* .bss section which is used for uninitialized data */ .bss (NOLOAD) : { __bss_start = . ; __bss_start__ = . ; *(.bss) *(.bss.*) *(.gnu.linkonce.b.*) *(COMMON) . = ALIGN(4); } >DATA . = ALIGN(4); __bss_end__ = . ; _end = .; PROVIDE (end = .); /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } } nxt-firmware-1.29.7/lib/sscanf.c000066400000000000000000000033731466344546000164460ustar00rootroot00000000000000/* * Copyright (C) 2010 Nicolas Schodet * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* NXT source code is using sscanf to parse a float. Newlib sscanf will pull * too many code, so here is a stub which implement just what is used. */ #include #include #include int sscanf (const char *str, const char *fmt, ...) { va_list ap; float *f; char *tailptr; /* Only support use in NXT source code. */ if (fmt[0] != '%' || fmt[1] != 'f' || fmt[2] != '\0') return 0; /* Retrieve float pointer. */ va_start (ap, fmt); f = va_arg (ap, float *); va_end (ap); /* Parse using the nice strtod. */ *f = strtod (str, &tailptr); if (str == tailptr) return 0; else return 1; } nxt-firmware-1.29.7/src/000077500000000000000000000000001466344546000150405ustar00rootroot00000000000000nxt-firmware-1.29.7/src/BtTest.inc000066400000000000000000001353431466344546000167510ustar00rootroot00000000000000//******* TestPrg ************************************************************ //#define TESTPRG // If defined the test program will be included #ifndef BUILD_DATE # define BUILD_DATE "" #endif #ifdef TESTPRG #include "Test1.h" #include "Test2.h" #endif extern void BtIo(void); void GetProtocolVersion(UBYTE *String) { char Tmp[DISPLAYLINE_LENGTH + 1]; snprintf(Tmp, sizeof(Tmp), "%d.%d.%d", (FIRMWAREVERSION >> 8) & 0xFF, FIRMWAREVERSION & 0xFF, FIRMWAREPATCH); snprintf((char*)String, DISPLAYLINE_LENGTH + 1, "FWi %*s", DISPLAYLINE_LENGTH - 4, Tmp); } void GetARMBuild(UBYTE *String) { snprintf((char*)String, DISPLAYLINE_LENGTH + 1, "%s", BUILD_DATE); } void GetBC4Build(UBYTE *String) { sprintf((char*)String,"BC4 %2X.%02X",pMapComm->BrickData.BluecoreVersion[1],pMapComm->BrickData.BluecoreVersion[0]); } void GetAVRBuild(UBYTE *String) { sprintf((char*)String,"AVR %1u.%02u",((IoFromAvr.Battery >> 13) & 3),((IoFromAvr.Battery >> 10) & 7)); } void GetBC4Address(UBYTE *String) { UWORD Count; UBYTE Tmp; Count = (UWORD)sprintf((char*)String,"ID "); for (Tmp = 0;(Tmp < (SIZE_OF_BDADDR - 1)) && (Count <= (DISPLAYLINE_LENGTH - 2));Tmp++) { Count += (UWORD)sprintf((char*)&String[Count],"%02X",(UWORD)(pMapComm->BrickData.BdAddr[Tmp]) & 0xFF); } } enum TSTPRG { SYSTEM_INIT = 1, SYSTEM_UNLOCK_INIT, SYSTEM_UNLOCK, SYSTEM_PAGE, TIMER_INIT, TIMER_SHOW, TIMER_HOLD, BT_PAGE, BT_RESET, BT_RESETTING, BT_LIST_INIT, BT_LIST, BT_CONN_INIT, BT_CONN, BT_UPDATE_FW, TSTPRG_INIT, TSTPRG_SELECT_SUBTEST, TSTPRG_SENSOR_INIT, TSTPRG_SELECT_SENSOR, TSTPRG_TOUCH_SENSOR_INIT, TSTPRG_TOUCH_SENSOR, TSTPRG_SOUND_SENSOR_SELECT, TSTPRG_SOUND_SENSOR_INIT, TSTPRG_SOUND_SENSOR, TSTPRG_LIGHT_SENSOR_SELECT, TSTPRG_LIGHT_SENSOR_INIT, TSTPRG_LIGHT_SENSOR, TSTPRG_SKIP_SENSOR, TSTPRG_RCX_INIT, TSTPRG_RCX_SELECT, TSTPRG_RCX_DISPLAY_INIT, TSTPRG_RCX_DISPLAY, TSTPRG_RCX_INPUT_SELECT, TSTPRG_RCX_INPUT_INIT, TSTPRG_RCX_INPUT, TSTPRG_RCX_DIGITAL_INIT, TSTPRG_RCX_DIGITAL_OK, TSTPRG_RCX_DIGITAL_FAIL, TSTPRG_RCX_DIGITAL, TSTPRG_RCX_MOTOR_INIT, TSTPRG_RCX_MOTOR, TSTPRG_SKIP_RCX_MOTOR, TSTPRG_SKIP_RCX, TSTPRG_MOTOR_INIT, TSTPRG_MOTOR, TSTPRG_SKIP_MOTOR, TSTPRG_SKIP, TSTPRG_WAIT }; const UBYTE TXT_EMPTY[] = " "; const UBYTE TXT_LINE[] = "----------------"; #ifdef TESTPRG const UBYTE TXT_TEST[] = "Timer Test Bt "; const UBYTE TXT_TIMER[] = "Reset Hold "; const UBYTE TXT_TIMER_HOLD[] = " Continue "; const UBYTE TXT_LAST[] = "Last UI->BT Cmd."; const UBYTE TXT_BT_PAGE[] = "Reset List BtIo"; const UBYTE TXT_RESETTING[] = " Resetting! "; const UBYTE TXT_BT_LIST[] = "Down ConTab Up "; const UBYTE TXT_BT_CONN[] = "Down Update Up "; const UBYTE TXT_BTUPDATE[] = "BT update mode !"; const UBYTE TXT_DONE[] = " When done "; const UBYTE TXT_RESET[] = " activate reset "; const UBYTE TXT_REBOOT[] = "button to reboot"; const UBYTE TXT_TESTPRG[] = " TestPrg V0.08 "; const UBYTE TXT_SELECT[] = "Select sub test "; const UBYTE TXT_SUBTEST[] = "Sens. RCX Motor"; const UBYTE TXT_SELECT_SENSOR[] = " Select sensor "; const UBYTE TXT_SENSORS[] = "Touch Snd. Light"; const UBYTE TXT_SELECT_TYPE[] = " Select type "; const UBYTE TXT_SOUND_SENSORS[] = " DB DBA "; const UBYTE TXT_LIGHT_SENSORS[] = "Pasive Active"; const UBYTE TXT_SENSOR_TOUCH[] = "Touch Sensor Tst"; const UBYTE TXT_SENSOR_SOUND_DB[] = "DB Sound Sens."; const UBYTE TXT_SENSOR_SOUND_DBA[] = "DBA Sound Sens."; const UBYTE TXT_SOUND_STOP[] = "440Hz Stop 4KHz"; const UBYTE TXT_SENSOR_LIGHT_PASIVE[] = "Pas. Light Sens."; const UBYTE TXT_SENSOR_LIGHT_ACTIVE[] = "Act. Light Sens."; const UBYTE TXT_SUBTEST_STOP[] = " Stop "; const UBYTE TXT_MOTOR[] = " Motor test "; const UBYTE TXT_MOTOR_HEADER[] = "Outp Set Cnt"; const UBYTE TXT_MOTOR_STOP[] = "Bwd Stop Fwd"; const UBYTE TXT_RCX[] = " RCX test "; const UBYTE TXT_RCX_STOP[] = "Inp Disp Outp"; const UBYTE TXT_RCX_INPUT_PASIVE[] = "Input pasive Tst"; const UBYTE TXT_RCX_INPUT_ACTIVE[] = "Input active Tst"; const UBYTE TXT_RCX_INPUT_SELECT[] = "Pas. Act. Dig."; const UBYTE TXT_RCX_INPUT_DIGITAL[] = "Digital I/O Tst"; const UBYTE TXT_RCX_DIGITAL_OK[] = " OK "; const UBYTE TXT_RCX_DIGITAL_FAIL[] = " FAIL "; const UBYTE TXT_MOTOR_NEXT[] = "Bwd Next Fwd"; void TestPrgRunMotor(UBYTE No,SBYTE Speed) { pMapOutPut->Outputs[No].Mode = MOTORON | BRAKE; pMapOutPut->Outputs[No].Speed = Speed; pMapOutPut->Outputs[No].TachoLimit = 0; pMapOutPut->Outputs[No].RunState = MOTOR_RUN_STATE_RUNNING; pMapOutPut->Outputs[No].Flags = UPDATE_MODE | UPDATE_SPEED | UPDATE_TACHO_LIMIT; } void TestPrgFloatMotor(UBYTE No) { pMapOutPut->Outputs[No].Mode = 0; pMapOutPut->Outputs[No].Speed = 0; pMapOutPut->Outputs[No].TachoLimit = 0; pMapOutPut->Outputs[No].RunState = MOTOR_RUN_STATE_RUNNING; pMapOutPut->Outputs[No].Flags = UPDATE_MODE | UPDATE_SPEED | UPDATE_TACHO_LIMIT; } SBYTE TestPrgReadMotor(UBYTE No) { return ((SBYTE)(pMapOutPut->Outputs[No].TachoCnt / 360)); } #endif UBYTE TestPrg(UBYTE Dummy) { static UWORD Count; static UBYTE TxtBuffer[TEXTLINES][DISPLAYLINE_LENGTH + 1]; static UBYTE State = SYSTEM_INIT; #ifdef TESTPRG static UWORD Pointer; static UWORD InputValues[NO_OF_INPUTS]; static SWORD OutputValues[NO_OF_OUTPUTS]; static UBYTE VolumeSave; static UBYTE Timer; static UBYTE SubState = 0; UBYTE Tmp; #endif Dummy = Dummy; switch (State) { case SYSTEM_INIT : { GetProtocolVersion(TxtBuffer[0]); GetARMBuild(TxtBuffer[1]); GetAVRBuild(TxtBuffer[2]); GetBC4Build(TxtBuffer[3]); GetBC4Address(TxtBuffer[4]); pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TxtBuffer[0]; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TxtBuffer[1]; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TxtBuffer[2]; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TxtBuffer[3]; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TxtBuffer[4]; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_EMPTY; pMapDisplay->UpdateMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4) | TEXTLINE_BIT(TEXTLINE_5) | TEXTLINE_BIT(TEXTLINE_6) | TEXTLINE_BIT(TEXTLINE_7) | TEXTLINE_BIT(TEXTLINE_8)); #ifdef TESTPRG SubState = 0; #endif State = SYSTEM_UNLOCK_INIT; } break; #ifndef TESTPRG case SYSTEM_UNLOCK_INIT : // ENTER * 1 + LEFT * 3 + RIGHT * 2 + ENTER * 1 = TEST MENU { if (cUiReadButtons() != BUTTON_NONE) { State = TSTPRG_SKIP; } } break; #else case SYSTEM_UNLOCK_INIT : // ENTER * 1 + LEFT * 3 + RIGHT * 2 + ENTER * 1 = TEST MENU { Tmp = cUiReadButtons(); switch (Tmp) { case BUTTON_ENTER : { switch (SubState) { case 0 : { SubState = 1; Pointer = 0; Count = 0; } break; case 3 : { State = SYSTEM_UNLOCK; } break; default : { Tmp = BUTTON_EXIT; } break; } } break; case BUTTON_NONE : { } break; default : { switch (SubState) { case 0 : { Tmp = BUTTON_EXIT; } break; case 1 : { if (Tmp == BUTTON_LEFT) { if (++Count >= 3) { Count = 0; SubState = 2; } Pointer = 0; } else { Tmp = BUTTON_EXIT; } } break; case 2 : { if (Tmp == BUTTON_RIGHT) { if (++Count >= 2) { SubState = 3; } Pointer = 0; } else { Tmp = BUTTON_EXIT; } } break; } } break; } Pointer++; if (((SubState) && (Pointer > 500)) || (Tmp == BUTTON_EXIT)) { State = TSTPRG_SKIP; } } break; case SYSTEM_UNLOCK : { pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_TEST; pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_8); State = SYSTEM_PAGE; } break; case SYSTEM_PAGE : { switch (cUiReadButtons()) { case BUTTON_ENTER : { IOMapUi.Flags &= ~UI_ENABLE_STATUS_UPDATE; pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); State = TSTPRG_INIT; } break; case BUTTON_EXIT : { Count = 0; State = TSTPRG_SKIP; } break; case BUTTON_LEFT : { IOMapUi.Flags &= ~UI_ENABLE_STATUS_UPDATE; pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_TIMER; pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TIMER_INIT; } break; case BUTTON_RIGHT : { IOMapUi.Flags &= ~UI_ENABLE_STATUS_UPDATE; pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_BT_PAGE; if (DISPLAYLINE_LENGTH >= 16) { sprintf((char*)TxtBuffer[2],"Command %02X",(UWORD)VarsUi.BTCommand & 0xFF); sprintf((char*)TxtBuffer[3],"Parameter 1 %02X",(UWORD)VarsUi.BTPar1 & 0xFF); sprintf((char*)TxtBuffer[4],"Parameter 2 %02X",(UWORD)VarsUi.BTPar2 & 0xFF); sprintf((char*)TxtBuffer[5],"Result %04X",(UWORD)VarsUi.BTResult); pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_LAST; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = TxtBuffer[2]; pMapDisplay->pTextLines[TEXTLINE_4] = TxtBuffer[3]; pMapDisplay->pTextLines[TEXTLINE_5] = TxtBuffer[4]; pMapDisplay->pTextLines[TEXTLINE_6] = TxtBuffer[5]; } pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = BT_PAGE; } break; } } break; case TIMER_INIT : { State = TIMER_SHOW; } break; case TIMER_SHOW : { sprintf((char*)TxtBuffer[2]," %10lu mS ",VarsUi.CRPasskey); pMapDisplay->pTextLines[TEXTLINE_3] = TxtBuffer[2]; pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); switch (cUiReadButtons()) { case BUTTON_ENTER : { pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_TIMER_HOLD; pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_8); State = TIMER_HOLD; } break; case BUTTON_LEFT : { pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_3); VarsUi.CRPasskey = 0L; } break; case BUTTON_EXIT : { State = TSTPRG_SKIP; } break; } } break; case TIMER_HOLD : { switch (cUiReadButtons()) { case BUTTON_ENTER : { pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_TIMER; pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_8); State = TIMER_SHOW; } break; case BUTTON_EXIT : { State = TSTPRG_SKIP; } break; } } break; case BT_PAGE : { switch (cUiReadButtons()) { case BUTTON_ENTER : { for (Count = 0;Count < TEXTLINES;Count++) { strcpy((char*)TxtBuffer[Count],(char*)TXT_EMPTY); pMapDisplay->pTextLines[TEXTLINE_1 + Count] = TxtBuffer[Count]; } pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_BT_LIST; pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; Pointer = 0; State = BT_LIST_INIT; } break; case BUTTON_EXIT : { Count = 0; State = TSTPRG_SKIP; } break; case BUTTON_LEFT : { State = BT_RESET; } break; case BUTTON_RIGHT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_BTUPDATE; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_DONE; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_RESET; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_REBOOT; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_EMPTY; pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; Timer = 0; State = BT_UPDATE_FW; } break; } } break; case BT_RESET : { VarsUi.BTCommand = (UBYTE)FACTORYRESET; VarsUi.BTPar1 = (UBYTE)0; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_RESETTING; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_EMPTY; pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = BT_RESETTING; } else { State = TSTPRG_SKIP; } } break; case BT_RESETTING : { if (VarsUi.BTResult != INPROGRESS) { State = TSTPRG_SKIP; } } break; case BT_UPDATE_FW : { if (++Timer >= 100) { BtIo(); } } break; case BT_LIST_INIT : { sprintf((char*)TxtBuffer[0],"DeviceTable No%2u",Pointer); sprintf((char*)TxtBuffer[2],"%-*.*s",DISPLAYLINE_LENGTH,DISPLAYLINE_LENGTH,(char*)pMapComm->BtDeviceTable[Pointer].Name); Count = (UWORD)sprintf((char*)TxtBuffer[3],"COD="); for (Tmp = 0;(Tmp < SIZE_OF_CLASS_OF_DEVICE) && (Count < (DISPLAYLINE_LENGTH - 2));Tmp++) { Count += (UWORD)sprintf((char*)&TxtBuffer[3][Count],"%02X",(UWORD)(pMapComm->BtDeviceTable[Pointer].ClassOfDevice[Tmp]) & 0xFF); } Count = (UWORD)sprintf((char*)TxtBuffer[4],"A="); for (Tmp = 0;(Tmp < SIZE_OF_BDADDR) && (Count <= (DISPLAYLINE_LENGTH - 2));Tmp++) { Count += (UWORD)sprintf((char*)&TxtBuffer[4][Count],"%02X",(UWORD)(pMapComm->BtDeviceTable[Pointer].BdAddr[Tmp]) & 0xFF); } sprintf((char*)TxtBuffer[5],"Status=%02X",(UWORD)(pMapComm->BtDeviceTable[Pointer].DeviceStatus) & 0xFF); pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = BT_LIST; } break; case BT_LIST : { switch (cUiReadButtons()) { case BUTTON_ENTER : { for (Count = 0;Count < TEXTLINES;Count++) { strcpy((char*)TxtBuffer[Count],(char*)TXT_EMPTY); pMapDisplay->pTextLines[TEXTLINE_1 + Count] = TxtBuffer[Count]; } pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_BT_CONN; pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; Pointer = 0; State = BT_CONN_INIT; } break; case BUTTON_EXIT : { State = SYSTEM_INIT; } break; case BUTTON_LEFT : { if (Pointer) { Pointer--; } else { Pointer = (SIZE_OF_BT_DEVICE_TABLE - 1); } State = BT_LIST_INIT; } break; case BUTTON_RIGHT : { if (Pointer < (SIZE_OF_BT_DEVICE_TABLE - 1)) { Pointer++; } else { Pointer = 0; } State = BT_LIST_INIT; } break; } } break; case BT_CONN_INIT : { sprintf((char*)TxtBuffer[0],"Conn. Table No%2u",Pointer); sprintf((char*)TxtBuffer[2],"%-*.*s",DISPLAYLINE_LENGTH,DISPLAYLINE_LENGTH,(char*)pMapComm->BtConnectTable[Pointer].Name); Count = (UWORD)sprintf((char*)TxtBuffer[3],"COD="); for (Tmp = 0;(Tmp < SIZE_OF_CLASS_OF_DEVICE) && (Count < (DISPLAYLINE_LENGTH - 2));Tmp++) { Count += (UWORD)sprintf((char*)&TxtBuffer[3][Count],"%02X",(UWORD)(pMapComm->BtConnectTable[Pointer].ClassOfDevice[Tmp]) & 0xFF); } Count = (UWORD)sprintf((char*)TxtBuffer[4],"A="); for (Tmp = 0;(Tmp < SIZE_OF_BDADDR) && (Count <= (DISPLAYLINE_LENGTH - 2));Tmp++) { Count += (UWORD)sprintf((char*)&TxtBuffer[4][Count],"%02X",(UWORD)(pMapComm->BtConnectTable[Pointer].BdAddr[Tmp]) & 0xFF); } sprintf((char*)TxtBuffer[5],"%-*.*s",DISPLAYLINE_LENGTH,DISPLAYLINE_LENGTH,(char*)pMapComm->BtConnectTable[Pointer].PinCode); if (DISPLAYLINE_LENGTH >= 16) { sprintf((char*)TxtBuffer[6],"H=%02X S=%02X Q=%02X",(UWORD)(pMapComm->BtConnectTable[Pointer].HandleNr) & 0xFF,(UWORD)(pMapComm->BtConnectTable[Pointer].StreamStatus) & 0xFF,(UWORD)(pMapComm->BtConnectTable[Pointer].LinkQuality) & 0xFF); } pMapDisplay->EraseMask |= TEXTLINE_BITS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = BT_CONN; } break; case BT_CONN : { switch (cUiReadButtons()) { case BUTTON_ENTER : { State = BT_CONN_INIT; } break; case BUTTON_EXIT : { State = SYSTEM_INIT; } break; case BUTTON_LEFT : { if (Pointer) { Pointer--; } else { Pointer = (SIZE_OF_BT_CONNECT_TABLE - 1); } State = BT_CONN_INIT; } break; case BUTTON_RIGHT : { if (Pointer < (SIZE_OF_BT_CONNECT_TABLE - 1)) { Pointer++; } else { Pointer = 0; } State = BT_CONN_INIT; } break; } } break; case TSTPRG_INIT : { IOMapUi.Flags &= ~UI_ENABLE_STATUS_UPDATE; pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_TESTPRG; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_SELECT; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SUBTEST; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_SELECT_SUBTEST; } break; case TSTPRG_SELECT_SUBTEST : { switch (cUiReadButtons()) { case BUTTON_LEFT : { State = TSTPRG_SENSOR_INIT; } break; case BUTTON_RIGHT : { State = TSTPRG_MOTOR_INIT; } break; case BUTTON_ENTER : { State = TSTPRG_RCX_INIT; } break; case BUTTON_EXIT : { Count = 0; State = TSTPRG_SKIP; } break; } } break; case TSTPRG_SENSOR_INIT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_TESTPRG; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_SELECT_SENSOR; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SENSORS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; for (Count = 0;Count < NO_OF_INPUTS;Count++) { InputValues[Count] = 0x7FFF; strcpy((char*)TxtBuffer[Count]," "); } State = TSTPRG_SELECT_SENSOR; } break; case TSTPRG_SELECT_SENSOR : { switch (cUiReadButtons()) { case BUTTON_LEFT : { State = TSTPRG_TOUCH_SENSOR_INIT; } break; case BUTTON_ENTER : { pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_SELECT_TYPE; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SOUND_SENSORS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_SOUND_SENSOR_SELECT; } break; case BUTTON_RIGHT : { pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_SELECT_TYPE; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_LIGHT_SENSORS; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_LIGHT_SENSOR_SELECT; } break; case BUTTON_EXIT : { State = TSTPRG_INIT; } break; } } break; case TSTPRG_TOUCH_SENSOR_INIT : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapDisplay->pTextLines[TEXTLINE_3 + Count] = TxtBuffer[Count]; } pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_SENSOR_TOUCH; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SUBTEST_STOP; pMapDisplay->UpdateMask |= TEXTLINE_BITS; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = SWITCH; } State = TSTPRG_TOUCH_SENSOR; } break; case TSTPRG_TOUCH_SENSOR : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { if (InputValues[Count] != pMapInput->Inputs[Count].ADRaw) { InputValues[Count] = pMapInput->Inputs[Count].ADRaw; sprintf((char*)TxtBuffer[Count]," Input %u = %4u ",(UWORD)Count + 1,(UWORD)InputValues[Count]); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3 + Count); } } if (cUiReadButtons() != BUTTON_NONE) { State = TSTPRG_SKIP_SENSOR; } } break; case TSTPRG_SOUND_SENSOR_SELECT : { switch (cUiReadButtons()) { case BUTTON_LEFT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_SENSOR_SOUND_DB; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = SOUND_DB; } State = TSTPRG_SOUND_SENSOR_INIT; } break; case BUTTON_ENTER : { State = TSTPRG_SKIP_SENSOR; } break; case BUTTON_EXIT : { State = TSTPRG_SKIP_SENSOR; } break; case BUTTON_RIGHT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_SENSOR_SOUND_DBA; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = SOUND_DBA; } State = TSTPRG_SOUND_SENSOR_INIT; } break; } } break; case TSTPRG_SOUND_SENSOR_INIT : { VolumeSave = pMapSound->Volume; pMapSound->Volume = MAX_VOLUME; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapDisplay->pTextLines[TEXTLINE_3 + Count] = TxtBuffer[Count]; } pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SOUND_STOP; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_SOUND_SENSOR; } break; case TSTPRG_SOUND_SENSOR : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { if (InputValues[Count] != pMapInput->Inputs[Count].ADRaw) { InputValues[Count] = pMapInput->Inputs[Count].ADRaw; sprintf((char*)TxtBuffer[Count]," Input %u = %4u ",(UWORD)Count + 1,(UWORD)InputValues[Count]); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3 + Count); } } switch (cUiReadButtons()) { case BUTTON_LEFT : { pMapSound->Freq = 440; pMapSound->Duration = 2000; pMapSound->Mode = SOUND_TONE; pMapSound->Flags |= SOUND_UPDATE; } break; case BUTTON_ENTER : { pMapSound->State = SOUND_STOP; pMapSound->Volume = VolumeSave; State = TSTPRG_SKIP_SENSOR; } break; case BUTTON_EXIT : { pMapSound->State = SOUND_STOP; pMapSound->Volume = VolumeSave; State = TSTPRG_SKIP_SENSOR; } break; case BUTTON_RIGHT : { pMapSound->Freq = 4000; pMapSound->Duration = 2000; pMapSound->Mode = SOUND_TONE; pMapSound->Flags |= SOUND_UPDATE; } break; } } break; case TSTPRG_LIGHT_SENSOR_SELECT : { switch (cUiReadButtons()) { case BUTTON_LEFT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_SENSOR_LIGHT_PASIVE; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = LIGHT_INACTIVE; } State = TSTPRG_LIGHT_SENSOR_INIT; } break; case BUTTON_ENTER : { State = TSTPRG_SKIP_SENSOR; } break; case BUTTON_EXIT : { State = TSTPRG_SKIP_SENSOR; } break; case BUTTON_RIGHT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_SENSOR_LIGHT_ACTIVE; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = LIGHT_ACTIVE; } State = TSTPRG_LIGHT_SENSOR_INIT; } break; } } break; case TSTPRG_LIGHT_SENSOR_INIT : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapDisplay->pTextLines[TEXTLINE_3 + Count] = TxtBuffer[Count]; } pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SUBTEST_STOP; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_LIGHT_SENSOR; } break; case TSTPRG_LIGHT_SENSOR : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { if (InputValues[Count] != pMapInput->Inputs[Count].ADRaw) { InputValues[Count] = pMapInput->Inputs[Count].ADRaw; sprintf((char*)TxtBuffer[Count]," Input %u = %4u ",(UWORD)Count + 1,(UWORD)InputValues[Count]); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3 + Count); } } if (cUiReadButtons() != BUTTON_NONE) { State = TSTPRG_SKIP_SENSOR; } } break; case TSTPRG_SKIP_SENSOR : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = NO_SENSOR; } State = TSTPRG_SENSOR_INIT; } break; case TSTPRG_MOTOR_INIT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_MOTOR; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_MOTOR_HEADER; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_MOTOR_STOP; for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; TestPrgRunMotor(Count,0); strcpy((char*)TxtBuffer[Count]," "); pMapDisplay->pTextLines[TEXTLINE_4 + Count] = TxtBuffer[Count]; } pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_MOTOR; } break; case TSTPRG_MOTOR : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { if (OutputValues[Count] != (SWORD)TestPrgReadMotor(Count)) { OutputValues[Count] = (SWORD)TestPrgReadMotor(Count); sprintf((char*)TxtBuffer[Count]," %c %-4d %4d",(char)Count + 'A',(SWORD)pMapOutPut->Outputs[Count].Speed,OutputValues[Count]); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_4 + Count); } } switch (cUiReadButtons()) { case BUTTON_LEFT : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; TestPrgRunMotor(Count,-50); } } break; case BUTTON_ENTER : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; TestPrgRunMotor(Count,0); } } break; case BUTTON_EXIT : { State = TSTPRG_SKIP_MOTOR; } break; case BUTTON_RIGHT : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; TestPrgRunMotor(Count,50); } } break; } } break; case TSTPRG_SKIP_MOTOR : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { TestPrgFloatMotor(Count); } State = TSTPRG_INIT; } break; case TSTPRG_RCX_INIT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_RCX; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_SELECT; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_RCX_STOP; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_RCX_SELECT; } break; case TSTPRG_RCX_SELECT : { switch (cUiReadButtons()) { case BUTTON_LEFT : { pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_SELECT_TYPE; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_RCX_INPUT_SELECT; pMapDisplay->UpdateMask |= TEXTLINE_BITS; for (Count = 0;Count < NO_OF_INPUTS;Count++) { InputValues[Count] = 0x7FFF; strcpy((char*)TxtBuffer[Count]," "); } State = TSTPRG_RCX_INPUT_SELECT; } break; case BUTTON_ENTER : { State = TSTPRG_RCX_DISPLAY_INIT; } break; case BUTTON_RIGHT : { State = TSTPRG_RCX_MOTOR_INIT; } break; case BUTTON_EXIT : { State = TSTPRG_INIT; } break; } } break; case TSTPRG_RCX_DISPLAY_INIT : { Count = 0; pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); State = TSTPRG_RCX_DISPLAY; } break; case TSTPRG_RCX_DISPLAY : { if ((Count & 0x7FF) == 0x000) { pMapDisplay->pScreens[SCREEN_BACKGROUND] = (BMPMAP*)Test1; pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); } if ((Count & 0x7FF) == 0x3FF) { pMapDisplay->pScreens[SCREEN_BACKGROUND] = (BMPMAP*)Test2; pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); } Count++; if (cUiReadButtons() != BUTTON_NONE) { State = TSTPRG_SKIP_RCX; } } break; case TSTPRG_RCX_INPUT_SELECT : { switch (cUiReadButtons()) { case BUTTON_LEFT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_RCX_INPUT_PASIVE; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = SWITCH; } State = TSTPRG_RCX_INPUT_INIT; } break; case BUTTON_ENTER : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_RCX_INPUT_ACTIVE; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = REFLECTION; } State = TSTPRG_RCX_INPUT_INIT; } break; case BUTTON_EXIT : { State = TSTPRG_SKIP_RCX; } break; case BUTTON_RIGHT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_RCX_INPUT_DIGITAL; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SUBTEST_STOP; pMapDisplay->UpdateMask |= TEXTLINE_BITS; for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = CUSTOM; } SubState = 0; Timer = 0; State = TSTPRG_RCX_DIGITAL_INIT; } break; } } break; case TSTPRG_RCX_INPUT_INIT : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapDisplay->pTextLines[TEXTLINE_3 + Count] = TxtBuffer[Count]; } pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_SUBTEST_STOP; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_RCX_INPUT; Timer = 0; } break; case TSTPRG_RCX_INPUT : { if (++Timer >= 250) { Timer = 0; for (Count = 0;Count < NO_OF_INPUTS;Count++) { if (InputValues[Count] != pMapInput->Inputs[Count].ADRaw) { InputValues[Count] = pMapInput->Inputs[Count].ADRaw; sprintf((char*)TxtBuffer[Count]," Input %u = %4u ",(UWORD)Count + 1,(UWORD)InputValues[Count]); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3 + Count); } } } if (cUiReadButtons() != BUTTON_NONE) { State = TSTPRG_SKIP_RCX; } } break; case TSTPRG_RCX_DIGITAL_INIT : { if (++Timer >= 20) { Timer = 0; switch (SubState) { case 0 : { pMapInput->Inputs[0].DigiPinsDir |= DIGI0; // Digi 1-0 output -, pMapInput->Inputs[0].DigiPinsDir &= ~DIGI1; // Digi 1-1 input -, | pMapInput->Inputs[2].DigiPinsDir &= ~DIGI0; // Digi 3-0 input | -' pMapInput->Inputs[2].DigiPinsDir |= DIGI1; // Digi 3-1 output -' pMapInput->Inputs[0].DigiPinsOut |= DIGI0; // Digi 1-0 output high pMapInput->Inputs[2].DigiPinsOut &= ~DIGI1; // Digi 3-1 output low SubState++; } break; case 1 : { if ((pMapInput->Inputs[2].DigiPinsIn & DIGI0) && !(pMapInput->Inputs[0].DigiPinsIn & DIGI1)) { pMapInput->Inputs[0].DigiPinsOut &= ~DIGI0; // Digi 1-0 output low pMapInput->Inputs[2].DigiPinsOut |= DIGI1; // Digi 3-1 output high SubState++; } else { State = TSTPRG_RCX_DIGITAL_FAIL; } } break; case 2 : { if (!(pMapInput->Inputs[2].DigiPinsIn & DIGI0) && (pMapInput->Inputs[0].DigiPinsIn & DIGI1)) { pMapInput->Inputs[0].DigiPinsDir &= ~DIGI0; // Digi 1-0 input pMapInput->Inputs[0].DigiPinsDir &= ~DIGI1; // Digi 1-1 input pMapInput->Inputs[2].DigiPinsDir &= ~DIGI0; // Digi 3-0 input pMapInput->Inputs[2].DigiPinsDir &= ~DIGI1; // Digi 3-1 input pMapInput->Inputs[1].DigiPinsDir |= DIGI0; // Digi 2-0 output -, pMapInput->Inputs[1].DigiPinsDir &= ~DIGI1; // Digi 2-1 input -, | pMapInput->Inputs[3].DigiPinsDir &= ~DIGI0; // Digi 4-0 input | -' pMapInput->Inputs[3].DigiPinsDir |= DIGI1; // Digi 4-1 output -' pMapInput->Inputs[1].DigiPinsOut |= DIGI0; // Digi 2-0 output high pMapInput->Inputs[3].DigiPinsOut &= ~DIGI1; // Digi 4-1 output low SubState++; } else { State = TSTPRG_RCX_DIGITAL_FAIL; } } break; case 3 : { if ((pMapInput->Inputs[3].DigiPinsIn & DIGI0) && !(pMapInput->Inputs[1].DigiPinsIn & DIGI1)) { pMapInput->Inputs[1].DigiPinsOut &= ~DIGI0; // Digi 2-0 output low pMapInput->Inputs[3].DigiPinsOut |= DIGI1; // Digi 4-1 output high SubState++; } else { State = TSTPRG_RCX_DIGITAL_FAIL; } } break; case 4 : { if (!(pMapInput->Inputs[3].DigiPinsIn & DIGI0) && (pMapInput->Inputs[1].DigiPinsIn & DIGI1)) { pMapInput->Inputs[1].DigiPinsDir &= ~DIGI0; // Digi 2-0 input pMapInput->Inputs[1].DigiPinsDir &= ~DIGI1; // Digi 2-1 input pMapInput->Inputs[3].DigiPinsDir &= ~DIGI0; // Digi 4-0 input pMapInput->Inputs[3].DigiPinsDir &= ~DIGI1; // Digi 4-1 input State = TSTPRG_RCX_DIGITAL_OK; } else { State = TSTPRG_RCX_DIGITAL_FAIL; } } break; } } } break; case TSTPRG_RCX_DIGITAL_OK : { pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_RCX_DIGITAL_OK; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_RCX_DIGITAL; } break; case TSTPRG_RCX_DIGITAL_FAIL : { pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_RCX_DIGITAL_FAIL; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_RCX_DIGITAL; } break; case TSTPRG_RCX_DIGITAL : { if (cUiReadButtons() != BUTTON_NONE) { State = TSTPRG_SKIP_RCX; } } break; case TSTPRG_RCX_MOTOR_INIT : { pMapDisplay->pTextLines[TEXTLINE_1] = (UBYTE*)TXT_MOTOR; pMapDisplay->pTextLines[TEXTLINE_2] = (UBYTE*)TXT_LINE; pMapDisplay->pTextLines[TEXTLINE_3] = (UBYTE*)TXT_MOTOR_HEADER; pMapDisplay->pTextLines[TEXTLINE_4] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_5] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_6] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_7] = (UBYTE*)TXT_EMPTY; pMapDisplay->pTextLines[TEXTLINE_8] = (UBYTE*)TXT_MOTOR_NEXT; for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; TestPrgRunMotor(Count,0); strcpy((char*)TxtBuffer[Count]," "); pMapDisplay->pTextLines[TEXTLINE_4 + Count] = TxtBuffer[Count]; } Pointer = 0; pMapDisplay->UpdateMask |= TEXTLINE_BITS; State = TSTPRG_RCX_MOTOR; } break; case TSTPRG_RCX_MOTOR : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { if (OutputValues[Count] != (SWORD)TestPrgReadMotor(Count)) { OutputValues[Count] = (SWORD)TestPrgReadMotor(Count); if (Pointer == Count) { sprintf((char*)TxtBuffer[Count],"> %c %-4d %4d",(char)Count + 'A',(SWORD)pMapOutPut->Outputs[Count].Speed,OutputValues[Count]); } else { sprintf((char*)TxtBuffer[Count]," %c %-4d %4d",(char)Count + 'A',(SWORD)pMapOutPut->Outputs[Count].Speed,OutputValues[Count]); } pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_4 + Count); } } switch (cUiReadButtons()) { case BUTTON_LEFT : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; if (Pointer == Count) { TestPrgRunMotor(Count,-50); } else { TestPrgRunMotor(Count,0); } } } break; case BUTTON_ENTER : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; TestPrgRunMotor(Count,0); } if (++Pointer >= NO_OF_OUTPUTS) { State = TSTPRG_SKIP_RCX_MOTOR; } } break; case BUTTON_EXIT : { State = TSTPRG_SKIP_RCX_MOTOR; } break; case BUTTON_RIGHT : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { OutputValues[Count] = 0x7FFF; if (Pointer == Count) { TestPrgRunMotor(Count,50); } else { TestPrgRunMotor(Count,0); } } } break; } } break; case TSTPRG_SKIP_RCX_MOTOR : { for (Count = 0;Count < NO_OF_OUTPUTS;Count++) { TestPrgFloatMotor(Count); } State = TSTPRG_RCX_INIT; } break; case TSTPRG_SKIP_RCX : { for (Count = 0;Count < NO_OF_INPUTS;Count++) { pMapInput->Inputs[Count].SensorType = NO_SENSOR; } pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); State = TSTPRG_RCX_INIT; } break; #endif case TSTPRG_SKIP : { Count++; if (Count == 100) { pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); } if (Count >= 200) { IOMapUi.Flags |= UI_REDRAW_STATUS; State = 0; } } break; default : { State = SYSTEM_INIT; } break; } return (State); } nxt-firmware-1.29.7/src/Functions.inl000066400000000000000000004060111466344546000175160ustar00rootroot00000000000000// // Programmer // // Date init 26.04.2005 // // Reviser $Author:: Dktochpe $ // // Revision date $Date:: $ // // Filename $Workfile:: $ // // Version $Revision:: $ // // Archive $Archive:: $ // // Platform C // //******* cUiBtTest ********************************************************** const UBYTE NONVOLATILE_NAME[] = UI_NONVOLATILE; // Non volatile filename without extention const UBYTE DEFAULT_PROGRAM_NAME[] = UI_PROGRAM_DEFAULT; // On brick programming filename without extention const UBYTE TEMP_PROGRAM_FILENAME[] = UI_PROGRAM_TEMP; // On brick programming tmp filename without extention const UBYTE VM_PROGRAM_READER[] = UI_PROGRAM_READER; // On brick programming script reader filename without extention const UBYTE TEMP_DATALOG_FILENAME[] = UI_DATALOG_TEMP; // On brick datalog tmp filename without extention const UBYTE DEFAULT_DATALOG_NAME[] = UI_DATALOG_DEFAULT; // On brick datalog filename without extention const UBYTE DEFAULT_PIN_CODE[] = UI_PINCODE_DEFAULT; // Default blue tooth pin code const UBYTE TXT_INVALID_SENSOR[] = "??????????????"; // Display invalid sensor data #define SENSORS (MENU_SENSOR_INVALID - MENU_SENSOR_EMPTY) const UBYTE SENSORTYPE[SENSORS] = // for view and datalog { 0, // MENU_SENSOR_EMPTY SOUND_DB, // MENU_SENSOR_SOUND_DB SOUND_DBA, // MENU_SENSOR_SOUND_DBA LIGHT_ACTIVE, // MENU_SENSOR_LIGHT LIGHT_INACTIVE, // MENU_SENSOR_LIGHT_AMB SWITCH, // MENU_SENSOR_TOUCH 0, // MENU_SENSOR_MOTOR_DEG 0, // MENU_SENSOR_MOTOR_ROT LOWSPEED_9V, // MENU_SENSOR_ULTRASONIC_IN LOWSPEED_9V, // MENU_SENSOR_ULTRASONIC_CM LOWSPEED_9V, // MENU_SENSOR_IIC_TEMP_C LOWSPEED_9V, // MENU_SENSOR_IIC_TEMP_F COLORFULL // MENU_SENSOR_COLOR }; const UBYTE SENSORMODE[SENSORS] = // for view and datalog { 0, // MENU_SENSOR_EMPTY PCTFULLSCALEMODE, // MENU_SENSOR_SOUND_DB PCTFULLSCALEMODE, // MENU_SENSOR_SOUND_DBA PCTFULLSCALEMODE, // MENU_SENSOR_LIGHT PCTFULLSCALEMODE, // MENU_SENSOR_LIGHT_AMB BOOLEANMODE, // MENU_SENSOR_TOUCH 0, // MENU_SENSOR_MOTOR_DEG 0, // MENU_SENSOR_MOTOR_ROT 0, // MENU_SENSOR_ULTRASONIC_IN 0, // MENU_SENSOR_ULTRASONIC_CM 0, // MENU_SENSOR_IIC_TEMP_C 0, // MENU_SENSOR_IIC_TEMP_F 0 // MENU_SENSOR_COLOR }; const UBYTE SENSORFORMAT[SENSORS][9] = { "", // MENU_SENSOR_EMPTY "%3.0f %%", // MENU_SENSOR_SOUND_DB "%3.0f %%", // MENU_SENSOR_SOUND_DBA "%3.0f %%", // MENU_SENSOR_LIGHT "%3.0f %%", // MENU_SENSOR_LIGHT_AMB "%1.0f", // MENU_SENSOR_TOUCH "%8.0f `", // MENU_SENSOR_MOTOR_DEG "%8.0f R", // MENU_SENSOR_MOTOR_ROT "%3.0f In", // MENU_SENSOR_ULTRASONIC_IN "%3.0f cm", // MENU_SENSOR_ULTRASONIC_CM "%5.1f `C", // MENU_SENSOR_IIC_TEMP_C "%5.1f `F", // MENU_SENSOR_IIC_TEMP_F "%9.0f" // MENU_SENSOR_COLOR (no of characters) }; const float SENSORDIVIDER[SENSORS] = { 1.0f, // MENU_SENSOR_EMPTY 1.0f, // MENU_SENSOR_SOUND_DB 1.0f, // MENU_SENSOR_SOUND_DBA 1.0f, // MENU_SENSOR_LIGHT 1.0f, // MENU_SENSOR_LIGHT_AMB 1.0f, // MENU_SENSOR_TOUCH 1.0f, // MENU_SENSOR_MOTOR_DEG 360.0f, // MENU_SENSOR_MOTOR_ROT 2.54f, // MENU_SENSOR_ULTRASONIC_IN 1.0f, // MENU_SENSOR_ULTRASONIC_CM 10.0f, // MENU_SENSOR_IIC_TEMP_C 10.0f, // MENU_SENSOR_IIC_TEMP_F 1.0f // MENU_SENSOR_COLOR }; #define SENSORSYNCDATA "Sync data" #define SENSORSDATA "Sdata" #define SENSORTIME "Time" const UBYTE SENSORDIRNAME[SENSORS - 1][19] = { "Sound Sensor", // MENU_SENSOR_SOUND_DB "Sound Sensor", // MENU_SENSOR_SOUND_DBA "Light Sensor", // MENU_SENSOR_LIGHT "Light Sensor", // MENU_SENSOR_LIGHT_AMB "Bumper", // MENU_SENSOR_TOUCH "FP Rotation Sensor", // MENU_SENSOR_MOTOR_DEG "FP Rotation Sensor", // MENU_SENSOR_MOTOR_ROT "Distance Sensor", // MENU_SENSOR_ULTRASONIC_IN "Distance Sensor", // MENU_SENSOR_ULTRASONIC_CM "NXT Temp Sensor", // MENU_SENSOR_IIC_TEMP_C "NXT Temp Sensor", // MENU_SENSOR_IIC_TEMP_F "Color Detector" // MENU_SENSOR_COLOR }; const UBYTE SENSORUNITNAME[SENSORS - 1][5] = { "_dB", // MENU_SENSOR_SOUND_DB "_dBa", // MENU_SENSOR_SOUND_DBA "_on", // MENU_SENSOR_LIGHT "_off", // MENU_SENSOR_LIGHT_AMB "", // MENU_SENSOR_TOUCH "_deg", // MENU_SENSOR_MOTOR_DEG "_rot", // MENU_SENSOR_MOTOR_ROT "_in", // MENU_SENSOR_ULTRASONIC_IN "_cm", // MENU_SENSOR_ULTRASONIC_CM "_C", // MENU_SENSOR_IIC_TEMP_C "_F", // MENU_SENSOR_IIC_TEMP_F "_0", // MENU_SENSOR_COLOR }; const UBYTE SENSORFORMAT2[SENSORS - 1][6] = { "\t%.0f", // MENU_SENSOR_SOUND_DB "\t%.0f", // MENU_SENSOR_SOUND_DBA "\t%.0f", // MENU_SENSOR_LIGHT "\t%.0f", // MENU_SENSOR_LIGHT_AMB "\t%.0f", // MENU_SENSOR_TOUCH "\t%.0f", // MENU_SENSOR_MOTOR_DEG "\t%.0f", // MENU_SENSOR_MOTOR_ROT "\t%.0f", // MENU_SENSOR_ULTRASONIC_IN "\t%.0f", // MENU_SENSOR_ULTRASONIC_CM "\t%.1f", // MENU_SENSOR_IIC_TEMP_C "\t%.1f", // MENU_SENSOR_IIC_TEMP_F "\t%.0f" // MENU_SENSOR_COLOR }; //******* cUiWriteLowspeed *************************************************** void cUiWriteLowspeed(UBYTE Port,UBYTE TxBytes,UBYTE *TxBuf,UBYTE RxBytes) { Port -= MENU_PORT_1; pMapLowSpeed->OutBuf[Port].InPtr = 0; pMapLowSpeed->OutBuf[Port].OutPtr = 0; while (TxBytes) { pMapLowSpeed->OutBuf[Port].Buf[pMapLowSpeed->OutBuf[Port].InPtr] = *TxBuf; pMapLowSpeed->OutBuf[Port].InPtr++; TxBuf++; TxBytes--; } pMapLowSpeed->InBuf[Port].BytesToRx = RxBytes; pMapLowSpeed->ChannelState[Port] = LOWSPEED_INIT; pMapLowSpeed->State |= (COM_CHANNEL_ONE_ACTIVE << Port); } //******* cUiReadLowspeed **************************************************** #define IIC_READY 0 #define IIC_BUSY 1 #define IIC_ERROR 2 UBYTE cUiReadLowspeed(UBYTE Port,UBYTE RxBytes,UWORD *Value) { UBYTE Result; *Value = 0; Port -= MENU_PORT_1; if ((pMapLowSpeed->ChannelState[Port] == LOWSPEED_IDLE) || (pMapLowSpeed->ChannelState[Port] == LOWSPEED_DONE)) { while (RxBytes) { (*Value) <<= 8; (*Value) |= (UWORD)(pMapLowSpeed->InBuf[Port].Buf[pMapLowSpeed->InBuf[Port].OutPtr]); pMapLowSpeed->InBuf[Port].OutPtr++; if (pMapLowSpeed->InBuf[Port].OutPtr >= SIZE_OF_LSBUF) { pMapLowSpeed->InBuf[Port].OutPtr = 0; } RxBytes--; } Result = IIC_READY; } else { if (pMapLowSpeed->ErrorType[Port] == LOWSPEED_CH_NOT_READY) { Result = IIC_ERROR; } else { Result = IIC_BUSY; } } return (Result); } //******* cUiUpdateSensor **************************************************** #define SENSOR_SETUP 0 #define SENSOR_ACQUIRE 3 #define SENSOR_READ 8 #define SENSOR_STATES 10 void cUiUpdateSensor(SWORD Time) { UBYTE Port; UBYTE Sensor; UBYTE Result; SWORD Tmp; if (VarsUi.SensorReset == TRUE) { for (Port = MENU_PORT_1;Port < MENU_PORT_INVALID;Port++) { VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = FALSE; } VarsUi.SensorTimer = (MIN_SENSOR_READ_TIME / SENSOR_STATES); VarsUi.SensorState = SENSOR_SETUP; } VarsUi.SensorTimer += Time; if (VarsUi.SensorTimer >= (MIN_SENSOR_READ_TIME / SENSOR_STATES)) { VarsUi.SensorTimer -= (MIN_SENSOR_READ_TIME / SENSOR_STATES); for (Port = MENU_PORT_1;Port < MENU_PORT_INVALID;Port++) { Sensor = VarsUi.DatalogPort[Port - MENU_PORT_1]; if (Sensor != MENU_SENSOR_EMPTY) { if ((Sensor == MENU_SENSOR_MOTOR_DEG) || (Sensor == MENU_SENSOR_MOTOR_ROT)) { if (VarsUi.SensorReset == TRUE) { pMapOutPut->Outputs[Port - MENU_PORT_A].Mode &= ~(BRAKE | MOTORON); pMapOutPut->Outputs[Port - MENU_PORT_A].Flags |= UPDATE_MODE | UPDATE_SPEED | UPDATE_RESET_COUNT; pMapOutPut->Outputs[Port - MENU_PORT_A].TachoCnt = 0; } if (VarsUi.SensorState == SENSOR_READ) { VarsUi.DatalogSampleValue[Port - MENU_PORT_1] = pMapOutPut->Outputs[Port - MENU_PORT_A].TachoCnt; VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = TRUE; } } else { pMapInput->Inputs[Port - MENU_PORT_1].SensorType = SENSORTYPE[Sensor - MENU_SENSOR_EMPTY]; pMapInput->Inputs[Port - MENU_PORT_1].SensorMode = SENSORMODE[Sensor - MENU_SENSOR_EMPTY]; if ((Sensor == MENU_SENSOR_ULTRASONIC_IN) || (Sensor == MENU_SENSOR_ULTRASONIC_CM)) { if (VarsUi.SensorReset == TRUE) { cUiWriteLowspeed(Port,3,(UBYTE *)"\x02\x41\x02",0); } if (VarsUi.SensorState == SENSOR_ACQUIRE) { cUiWriteLowspeed(Port,2,(UBYTE *)"\x02\x42",1); } if (VarsUi.SensorState == SENSOR_READ) { Result = cUiReadLowspeed(Port,1,(UWORD*)&Tmp); if (Result == IIC_READY) { if ((UBYTE)Tmp != 0xFF) { VarsUi.DatalogSampleValue[Port - MENU_PORT_1] = (SLONG)Tmp; VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = TRUE; } else { VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = FALSE; } } else { VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = FALSE; } } } else { if ((Sensor == MENU_SENSOR_IIC_TEMP_C) || (Sensor == MENU_SENSOR_IIC_TEMP_F)) { if (VarsUi.SensorState == SENSOR_SETUP) { cUiWriteLowspeed(Port,3,(UBYTE *)"\x98\x01\x60",0); } if (VarsUi.SensorState == SENSOR_ACQUIRE) { cUiWriteLowspeed(Port,2,(UBYTE *)"\x98\x00",2); } if (VarsUi.SensorState == SENSOR_READ) { Result = cUiReadLowspeed(Port,2,(UWORD*)&Tmp); if (Result == IIC_READY) { // if (Tmp >= -14080) { if (Sensor == MENU_SENSOR_IIC_TEMP_F) { VarsUi.DatalogSampleValue[Port - MENU_PORT_1] = (SLONG)((float)(Tmp + 4544) / 14.2f); } else { VarsUi.DatalogSampleValue[Port - MENU_PORT_1] = (SLONG)((float)Tmp / 25.6f); } VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = TRUE; } } else { if (Result == IIC_ERROR) { VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = FALSE; } } } } else { if (VarsUi.SensorState == SENSOR_READ) { if (pMapInput->Inputs[Port - MENU_PORT_1].InvalidData != INVALID_DATA) { VarsUi.DatalogSampleValue[Port - MENU_PORT_1] = pMapInput->Inputs[Port - MENU_PORT_1].SensorValue; VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = TRUE; } else { VarsUi.DatalogSampleValid[Port - MENU_PORT_1] = FALSE; } } } } } } } VarsUi.SensorState++; if (VarsUi.SensorState >= SENSOR_STATES) { VarsUi.SensorState = SENSOR_SETUP; } VarsUi.SensorReset = FALSE; } } //******* cUiGetCustomPctFullScale ******************************************* UBYTE cUiGetCustomPctFullScale(UBYTE Port,UBYTE Sensor) { UBYTE Result = 0; if ((Sensor != MENU_SENSOR_MOTOR_DEG) && (Sensor != MENU_SENSOR_MOTOR_ROT)) { Result = (UBYTE)pMapInput->Inputs[Port - MENU_PORT_1].CustomPctFullScale; } return (Result); } //******* cUiGetCustomActiveStatus ******************************************* UBYTE cUiGetCustomActiveStatus(UBYTE Port,UBYTE Sensor) { UBYTE Result = 0; if ((Sensor != MENU_SENSOR_MOTOR_DEG) && (Sensor != MENU_SENSOR_MOTOR_ROT)) { Result = (UBYTE)pMapInput->Inputs[Port - MENU_PORT_1].CustomActiveStatus; } return (Result); } //******* cUiPrintSensorInDisplayBuffer ************************************** #define COLORNAMES 6 const UBYTE COLORNAME[COLORNAMES][10] = { "1. Black ", "2. Blue ", "3. Green ", "4. Yellow", "5. Red ", "6. White " }; void cUiPrintSensorInDisplayBuffer(UBYTE Port) { UBYTE Sensor; float Value; SWORD Size; SWORD Index; Port -= MENU_PORT_1; Sensor = VarsUi.DatalogPort[Port] - MENU_SENSOR_EMPTY; Value = (float)VarsUi.DatalogSampleValue[Port] / (float)SENSORDIVIDER[Sensor]; Size = sprintf((char*)VarsUi.DisplayBuffer,(char*)SENSORFORMAT[Sensor],(float)0); sprintf((char*)VarsUi.DisplayBuffer,"%*.*s",Size,Size,(char*)TXT_INVALID_SENSOR); if (VarsUi.DatalogSampleValid[Port] == TRUE) { if (Sensor == (MENU_SENSOR_COLOR - MENU_SENSOR_EMPTY)) { Index = (SWORD)Value - 1; if ((Index >= 0) && (Index < COLORNAMES)) { sprintf((char*)VarsUi.DisplayBuffer,(char*)COLORNAME[Index]); } } else { if (Size < sprintf((char*)VarsUi.DisplayBuffer,(char*)SENSORFORMAT[Sensor],Value)) { sprintf((char*)VarsUi.DisplayBuffer,"%*.*s",Size,Size,(char*)TXT_INVALID_SENSOR); } } } } //******* cUiReleaseSensors ************************************************** void cUiReleaseSensors(void) { UBYTE Tmp; for (Tmp = 0;Tmp < NO_OF_INPUTS;Tmp++) { pMapInput->Inputs[Tmp].SensorType = NO_SENSOR; } } //******* cUiBtCommand ******************************************************* enum { UI_BT_CTRL, UI_BT_GET_DEVICES, // (UI_BT_GET_DEVICES,Known,*pDevices,NULL) [Known = 0,1] UI_BT_GET_DEVICE_NAME, // (UI_BT_GET_DEVICE_NAME,Known,*pIndex,*pDeviceName) [Known = 0,1] UI_BT_GET_DEVICE_TYPE, // (UI_BT_GET_DEVICE_TYPE,Known,*pIndex,*pDeviceType) [Known = 0,1] UI_BT_GET_CONNECTION_NAME, // (UI_BT_GET_CONNECTION_NAME,NULL,*pConnection,*pConnectionName) UI_BT_GET_CONNECTION_TYPE, // (UI_BT_GET_CONNECTION_TYPE,NULL,*pConnection,*pConnectionType) UI_BT_GET_CONNECTION_VALID, // (UI_BT_GET_CONNECTION_NAME,NULL,*pConnection,NULL) UI_BT_DUMMY }; #define UI_BT_FAILED 0x8200 // General command failed #define UI_BT_SUCCES 0x0000 // Command executed succesfully UBYTE cUiBTGetDeviceType(UBYTE *pCOD) { ULONG COD; UBYTE Result; UBYTE Tmp; COD = 0; for (Tmp = 0;Tmp < SIZE_OF_CLASS_OF_DEVICE;Tmp++) { COD <<= 8; COD |= (ULONG)*pCOD; pCOD++; } Result = DEVICETYPE_UNKNOWN; if ((COD & 0x00001FFF) == 0x00000804) { Result = DEVICETYPE_NXT; } if ((COD & 0x00001F00) == 0x00000200) { Result = DEVICETYPE_PHONE; } if ((COD & 0x00001F00) == 0x00000100) { Result = DEVICETYPE_PC; } return (Result); } UBYTE cUiBTGetDeviceIndex(UBYTE Known,UBYTE No,UBYTE *pIndex) { UBYTE Result = 0; UBYTE Tmp; *pIndex = 0; if (Known) { for (Tmp = 0;(Tmp < SIZE_OF_BT_DEVICE_TABLE) && (Result == 0);Tmp++) { if ((pMapComm->BtDeviceTable[Tmp].DeviceStatus & BT_DEVICE_KNOWN)) { if (No == *pIndex) { *pIndex = Tmp; Result = ~0; } else { (*pIndex)++; } } } } else { for (Tmp = 0;(Tmp < SIZE_OF_BT_DEVICE_TABLE) && (Result == 0);Tmp++) { if ((pMapComm->BtDeviceTable[Tmp].DeviceStatus & BT_DEVICE_UNKNOWN) || (pMapComm->BtDeviceTable[Tmp].DeviceStatus & BT_DEVICE_KNOWN)) { if (No == *pIndex) { *pIndex = Tmp; Result = ~0; } else { (*pIndex)++; } } } } return (Result); } UWORD cUiBTCommand(UBYTE Cmd,UBYTE Flag,UBYTE *pParam1,UBYTE *pParam2) { UWORD Result = UI_BT_FAILED; switch(Cmd) { case UI_BT_GET_DEVICES : { cUiBTGetDeviceIndex(Flag,SIZE_OF_BT_DEVICE_TABLE,pParam1); Result = UI_BT_SUCCES; } break; case UI_BT_GET_DEVICE_NAME : { if ((*pParam1 < SIZE_OF_BT_DEVICE_TABLE) && (pParam2 != NULL)) { pParam2[0] = 0; if (cUiBTGetDeviceIndex(Flag,*pParam1,&VarsUi.BTTmpIndex)) { sprintf((char*)pParam2,"%.*s",DISPLAYLINE_LENGTH,(char*)pMapComm->BtDeviceTable[VarsUi.BTTmpIndex].Name); Result = UI_BT_SUCCES; } } } break; case UI_BT_GET_DEVICE_TYPE : { if ((*pParam1 < SIZE_OF_BT_DEVICE_TABLE) && (pParam2 != NULL)) { pParam2[0] = 0; if (cUiBTGetDeviceIndex(Flag,*pParam1,&VarsUi.BTTmpIndex)) { pParam2[0] = cUiBTGetDeviceType(pMapComm->BtDeviceTable[VarsUi.BTTmpIndex].ClassOfDevice); Result = UI_BT_SUCCES; } } } break; case UI_BT_GET_CONNECTION_NAME : { if (*pParam1 < SIZE_OF_BT_CONNECT_TABLE) { if (pMapComm->BtConnectTable[*pParam1].Name[0]) { if (pParam2 != NULL) { sprintf((char*)pParam2,"%.*s",DISPLAYLINE_LENGTH,(char*)pMapComm->BtConnectTable[*pParam1].Name); } Result = UI_BT_SUCCES; } else { if (pParam2 != NULL) { pParam2[0] = 0; } } } } break; case UI_BT_GET_CONNECTION_TYPE : { if ((*pParam1 < SIZE_OF_BT_CONNECT_TABLE) && (pParam2 != NULL)) { pParam2[0] = 0; if (pMapComm->BtConnectTable[*pParam1].Name[0]) { pParam2[0] = cUiBTGetDeviceType(pMapComm->BtConnectTable[*pParam1].ClassOfDevice); Result = UI_BT_SUCCES; } } } break; case UI_BT_GET_CONNECTION_VALID : { if (*pParam1 < SIZE_OF_BT_CONNECT_TABLE) { if (pMapComm->BtConnectTable[*pParam1].Name[0]) { Result = UI_BT_SUCCES; } } } break; } return (Result); } #include "BtTest.inc" //******* cUiNVxxxxx ********************************************************* void cUiNVWrite(void) { sprintf((char*)VarsUi.NVFilename,"%s.%s",(char*)NONVOLATILE_NAME,(char*)TXT_SYS_EXT); VarsUi.NVTmpHandle = pMapLoader->pFunc(FINDFIRST,VarsUi.NVFilename,VarsUi.SearchFilenameBuffer,&VarsUi.NVTmpLength); if (!(VarsUi.NVTmpHandle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.NVTmpHandle,NULL,NULL); pMapLoader->pFunc(DELETE,VarsUi.NVFilename,NULL,NULL); } VarsUi.NVTmpLength = sizeof(NVDATA); VarsUi.NVTmpHandle = pMapLoader->pFunc(OPENWRITE,VarsUi.NVFilename,NULL,&VarsUi.NVTmpLength); pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.NVTmpHandle,(UBYTE*)&VarsUi.NVData,&VarsUi.NVTmpLength); pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.NVTmpHandle,NULL,NULL); } void cUiNVRead(void) { VarsUi.NVData.CheckByte = 0; sprintf((char*)VarsUi.NVFilename,"%s.%s",(char*)NONVOLATILE_NAME,(char*)TXT_SYS_EXT); VarsUi.NVTmpHandle = pMapLoader->pFunc(OPENREAD,VarsUi.NVFilename,NULL,&VarsUi.NVTmpLength); if (!(VarsUi.NVTmpHandle & 0x8000)) { VarsUi.NVTmpLength = sizeof(NVDATA); pMapLoader->pFunc(READ,(UBYTE*)&VarsUi.NVTmpHandle,(UBYTE*)&VarsUi.NVData,&VarsUi.NVTmpLength); pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.NVTmpHandle,NULL,NULL); } if (VarsUi.NVData.CheckByte != CHECKBYTE) { VarsUi.NVData.DatalogEnabled = DATALOGENABLED; VarsUi.NVData.VolumeStep = MAX_VOLUME; VarsUi.NVData.PowerdownCode = POWER_OFF_TIME_DEFAULT; VarsUi.NVData.DatalogNumber = 0; VarsUi.NVData.CheckByte = CHECKBYTE; cUiNVWrite(); } } //******* cUiFeedback ******************************************************** UBYTE cUiFeedback(BMPMAP *Bitmap,UBYTE TextNo1,UBYTE TextNo2,UWORD Time) // Show bimap and text { // if ((VarsUi.FBState == 0) || ((pMapDisplay->Flags & DISPLAY_POPUP) == 0)) { switch (VarsUi.FBState) { case 0 : // Set busy { VarsUi.FBState++; } break; case 1 : // Clear line 2,3,4 { if (DISPLAY_IDLE) { pMapDisplay->EraseMask |= (TEXTLINE_BIT(TEXTLINE_2) | TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4)); VarsUi.FBState++; } } break; case 2 : // Show bitmap if pressent { if (DISPLAY_IDLE) { if (Bitmap != NULL) { pMapDisplay->pBitmaps[BITMAP_1] = Bitmap; pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_1); } VarsUi.FBState++; } } break; case 3 : // Get text string { if (DISPLAY_IDLE) { pMapDisplay->UpdateMask |= SPECIAL_BIT(TOPLINE); VarsUi.FBText = cUiGetString(TextNo1); VarsUi.FBPointer = 0; if (TextNo2) { VarsUi.FBState = 5; } else { VarsUi.FBState++; } } } break; case 4 : // Show text string { if ((VarsUi.FBText[VarsUi.FBPointer]) && (VarsUi.FBPointer < NO_OF_FEEDBACK_CHARS)) { pMapDisplay->pFunc(DISPLAY_CHAR,TRUE,24 + VarsUi.FBPointer * 6,16,VarsUi.FBText[VarsUi.FBPointer],0); VarsUi.FBPointer++; } else { VarsUi.FBTimer = 0; VarsUi.FBState = 7; } } break; case 5 : // Show text string { if ((VarsUi.FBText[VarsUi.FBPointer]) && (VarsUi.FBPointer < NO_OF_FEEDBACK_CHARS)) { pMapDisplay->pFunc(DISPLAY_CHAR,TRUE,24 + VarsUi.FBPointer * 6,12,VarsUi.FBText[VarsUi.FBPointer],0); VarsUi.FBPointer++; } else { if (TextNo2 == 0xFF) { VarsUi.FBText = VarsUi.SelectedFilename; } else { VarsUi.FBText = cUiGetString(TextNo2); } VarsUi.FBPointer = 0; VarsUi.FBState++; } } break; case 6 : // Show text string { if ((VarsUi.FBText[VarsUi.FBPointer]) && (VarsUi.FBPointer < NO_OF_FEEDBACK_CHARS)) { pMapDisplay->pFunc(DISPLAY_CHAR,TRUE,24 + VarsUi.FBPointer * 6,20,VarsUi.FBText[VarsUi.FBPointer],0); VarsUi.FBPointer++; } else { VarsUi.FBTimer = 0; VarsUi.FBState++; } } break; case 7 : // Wait if time provided { if (++VarsUi.FBTimer >= (Time + 100)) { VarsUi.FBState++; } } break; default : // Exit { VarsUi.FBState = 0; } break; } } return (VarsUi.FBState); } //******* cUiFileList ******************************************************** UBYTE cUiFindNoOfFiles(UBYTE FileType,UBYTE *NoOfFiles) { switch (VarsUi.FNOFState) { case 0 : { *NoOfFiles = 0; if (FileType >= FILETYPES) { FileType = FILETYPE_ALL; } sprintf((char*)VarsUi.FNOFSearchBuffer,"*.%s",TXT_FILE_EXT[FileType]); VarsUi.FNOFHandle = pMapLoader->pFunc(FINDFIRST,VarsUi.FNOFSearchBuffer,VarsUi.FNOFNameBuffer,&VarsUi.FNOFLength); if (!(VarsUi.FNOFHandle & 0x8000)) { *NoOfFiles = 1; VarsUi.FNOFState++; } } break; case 1 : { VarsUi.FNOFHandle = pMapLoader->pFunc(FINDNEXT,(UBYTE*)&VarsUi.FNOFHandle,VarsUi.FNOFNameBuffer,&VarsUi.FNOFLength); if (!(VarsUi.FNOFHandle & 0x8000)) { *NoOfFiles += 1; } else { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.FNOFHandle,NULL,NULL); VarsUi.FNOFState = 0; } } break; } return (VarsUi.FNOFState); } UBYTE cUiFindNameForFileNo(UBYTE FileType,UBYTE FileNo,UBYTE *Name) { switch (VarsUi.FNOFState) { case 0 : { Name[0] = 0; if (FileNo) { if (FileType >= FILETYPES) { FileType = FILETYPE_ALL; } sprintf((char*)VarsUi.FNOFSearchBuffer,"*.%s",TXT_FILE_EXT[FileType]); VarsUi.FNOFHandle = pMapLoader->pFunc(FINDFIRST,VarsUi.FNOFSearchBuffer,Name,&VarsUi.FNOFLength); if (!(VarsUi.FNOFHandle & 0x8000)) { if (FileNo != 1) { VarsUi.FNOFFileNo = 1; VarsUi.FNOFState++; } else { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.FNOFHandle,NULL,NULL); } } } } break; case 1 : { VarsUi.FNOFHandle = pMapLoader->pFunc(FINDNEXT,(UBYTE*)&VarsUi.FNOFHandle,Name,&VarsUi.FNOFLength); if (!(VarsUi.FNOFHandle & 0x8000)) { VarsUi.FNOFFileNo++; if (FileNo == VarsUi.FNOFFileNo) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.FNOFHandle,NULL,NULL); VarsUi.FNOFState = 0; } } else { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.FNOFHandle,NULL,NULL); VarsUi.FNOFState = 0; } } break; } return (VarsUi.FNOFState); } UBYTE cUiFileList(UBYTE Action) // Show files and select { switch (Action) { case MENU_INIT : { if (!VarsUi.State) { VarsUi.FileCenter = 1; VarsUi.NextState = IOMapUi.State; } Action = MENU_DRAW; } break; case MENU_LEFT : { if (!VarsUi.State) { cUiListLeft(VarsUi.NoOfFiles,&VarsUi.FileCenter); VarsUi.NextState = TEST_BUTTONS; } Action = MENU_DRAW; } break; case MENU_RIGHT : { if (!VarsUi.State) { cUiListRight(VarsUi.NoOfFiles,&VarsUi.FileCenter); VarsUi.NextState = TEST_BUTTONS; } Action = MENU_DRAW; } break; case MENU_SELECT : { } break; default : { if (Action < FILETYPES) { if (!VarsUi.State) { VarsUi.FileType = Action; VarsUi.FileCenter = 1; VarsUi.NextState = IOMapUi.State; } Action = MENU_DRAW; } else { IOMapUi.State = EXIT_PRESSED; VarsUi.State = 0; } } break; } if (Action == MENU_DRAW) { switch (VarsUi.State) { case 0 : { VarsUi.FNOFState = 0; VarsUi.State++; } break; case 1 : { if (cUiFindNoOfFiles(VarsUi.FileType,&VarsUi.NoOfFiles) == 0) { if (VarsUi.NoOfFiles) { cUiListCalc(VarsUi.NoOfFiles,&VarsUi.FileCenter,&VarsUi.FileLeft,&VarsUi.FileRight); VarsUi.State++; } else { VarsUi.State = 0; IOMapUi.State = EXIT_PRESSED; } } } break; case 2 : { if (cUiFindNameForFileNo(VarsUi.FileType,VarsUi.FileCenter,VarsUi.SelectedFilename) == 0) { VarsUi.State++; } } break; default : { pMapDisplay->pMenuIcons[MENUICON_LEFT] = NULL; pMapDisplay->pMenuIcons[MENUICON_CENTER] = NULL; pMapDisplay->pMenuIcons[MENUICON_RIGHT] = NULL; if (VarsUi.FileLeft) { pMapDisplay->pMenuIcons[MENUICON_LEFT] = (UBYTE*)&Icons.Data[(VarsUi.FileType + ALLFILES) * Icons.ItemPixelsX * (Icons.ItemPixelsY / 8)]; pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_LEFT); } if (VarsUi.FileCenter) { pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Icons.Data[(VarsUi.FileType + ALLFILES) * Icons.ItemPixelsX * (Icons.ItemPixelsY / 8)]; pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_CENTER); } if (VarsUi.FileRight) { pMapDisplay->pMenuIcons[MENUICON_RIGHT] = (UBYTE*)&Icons.Data[(VarsUi.FileType + ALLFILES) * Icons.ItemPixelsX * (Icons.ItemPixelsY / 8)]; pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_RIGHT); } pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_5); // Search forward for termination VarsUi.Tmp = 0; while ((VarsUi.SelectedFilename[VarsUi.Tmp]) && (VarsUi.Tmp < FILENAME_LENGTH)) { VarsUi.Tmp++; } // Search backward for "." while ((VarsUi.Tmp) && (VarsUi.SelectedFilename[VarsUi.Tmp] != '.')) { VarsUi.Tmp--; } if (VarsUi.Tmp > DISPLAYLINE_LENGTH) { VarsUi.Tmp = DISPLAYLINE_LENGTH; } VarsUi.DisplayBuffer[VarsUi.Tmp] = 0; // Copy only name not ext while (VarsUi.Tmp) { VarsUi.Tmp--; VarsUi.DisplayBuffer[VarsUi.Tmp] = VarsUi.SelectedFilename[VarsUi.Tmp]; } pMapDisplay->pMenuText = VarsUi.DisplayBuffer; pMapDisplay->EraseMask |= MENUICON_BITS; pMapDisplay->UpdateMask |= (SPECIAL_BIT(FRAME_SELECT) | SPECIAL_BIT(MENUTEXT)); IOMapUi.State = VarsUi.NextState; VarsUi.State = 0; } break; } } return (VarsUi.State); } //******* cUiVolume ********************************************************** UBYTE cUiVolume(UBYTE Action) // MENU_INIT,MENU_LEFT,MENU_RIGHT,MENU_EXIT { switch (Action) { case MENU_INIT : // Init time counter and cursor bitmap { VarsUi.Counter = VarsUi.NVData.VolumeStep + 1; VarsUi.pTmp = (UBYTE*)&Cursor; for (VarsUi.Tmp = 0;(VarsUi.Tmp < SIZE_OF_CURSOR) && (VarsUi.Tmp < Cursor_size);VarsUi.Tmp++) { VarsUi.CursorTmp[VarsUi.Tmp] = *VarsUi.pTmp; VarsUi.pTmp++; } Action = MENU_DRAW; } break; case MENU_LEFT : // Dec { cUiListLeft(MAX_VOLUME + 1,&VarsUi.Counter); IOMapUi.Volume = VarsUi.Counter - 1; Action = MENU_DRAW; } break; case MENU_RIGHT : // Inc { cUiListRight(MAX_VOLUME + 1,&VarsUi.Counter); IOMapUi.Volume = VarsUi.Counter - 1; Action = MENU_DRAW; } break; case MENU_ENTER : // Enter { VarsUi.NVData.VolumeStep = VarsUi.Counter - 1; cUiNVWrite(); IOMapUi.Volume = VarsUi.NVData.VolumeStep; pMapSound->Volume = IOMapUi.Volume; Action = MENU_EXIT; } break; case MENU_EXIT : // Leave { IOMapUi.Volume = VarsUi.NVData.VolumeStep; } break; } if (Action == MENU_DRAW) { sprintf((char*)VarsUi.DisplayBuffer,"%u",(UWORD)VarsUi.Counter - 1); pMapDisplay->pTextLines[TEXTLINE_3] = VarsUi.DisplayBuffer; pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)VarsUi.CursorTmp; VarsUi.CursorTmp[4] = 46; VarsUi.CursorTmp[5] = 24; pMapDisplay->EraseMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4)); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= (TEXTLINE_BIT(TEXTLINE_3) | BITMAP_BIT(BITMAP_1)); } if (Action == MENU_EXIT) { IOMapUi.State = EXIT_PRESSED; } return (0); } //******* cUiGetUserString *************************************************** #define STRINGTYPES 2 #define TOPTEXT_LINE TEXTLINE_3 #define STRING_LINE TEXTLINE_5 typedef struct { const UBYTE Text; const UBYTE *Figures; const UBYTE NoOfFigures; const UBYTE MaxStringLength; const UBYTE WindowSize; const SBYTE DefaultPointer; } STRSETS; const UBYTE Figures[] = { "0987654321" "\x7F" "abcdefghijklmnopqrstuvwxyz " }; const STRSETS StrSets[STRINGTYPES] = { { TXT_GETUSERSTRING_PIN, Figures, 37, SIZE_OF_BT_PINCODE - 1, 15, 10 }, { TXT_GETUSERSTRING_FILENAME, Figures, 37, FILENAME_LENGTH - 4 , 15, 10 } }; UBYTE cUiGetUserString(UBYTE Type) // 0=Pincode, 1=filename { UBYTE Tmp1; SBYTE Tmp2; if (Type < STRINGTYPES) { switch (VarsUi.GUSState) { case 0 : // Init screen { // Disable update and prepare screen pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_LARGE); pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)&Ok; // Set figure pointer to default VarsUi.FigurePointer = (SBYTE)StrSets[Type].DefaultPointer; // Calculate cursor from default string VarsUi.GUSCursor = 0; while ((VarsUi.GUSCursor < DISPLAYLINE_LENGTH) && VarsUi.UserString[VarsUi.GUSCursor]) { VarsUi.GUSCursor++; } VarsUi.GUSNoname = TRUE; VarsUi.GUSState++; } break; case 1 : // Update user string { if (!(pMapDisplay->EraseMask & SCREEN_BIT(SCREEN_LARGE))) { // Display top text pMapDisplay->pTextLines[TOPTEXT_LINE] = cUiGetString(StrSets[Type].Text); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TOPTEXT_LINE); Tmp1 = 0; while (VarsUi.UserString[Tmp1] && (Tmp1 < StrSets[Type].MaxStringLength)) { VarsUi.DisplayText[Tmp1] = VarsUi.UserString[Tmp1]; Tmp1++; } if (Tmp1 < StrSets[Type].MaxStringLength) { VarsUi.DisplayText[Tmp1] = '_'; Tmp1++; } while (Tmp1 < StrSets[Type].MaxStringLength) { VarsUi.DisplayText[Tmp1] = ' '; Tmp1++; } VarsUi.DisplayText[Tmp1] = 0; pMapDisplay->pTextLines[STRING_LINE] = VarsUi.DisplayText; pMapDisplay->UpdateMask |= (TEXTLINE_BIT(STRING_LINE) | SPECIAL_BIT(TOPLINE)); pMapDisplay->EraseMask |= BITMAP_BIT(BITMAP_1); VarsUi.GUSState++; } } break; case 2 : // Update figure string { if (!(pMapDisplay->EraseMask & BITMAP_BIT(BITMAP_1))) { Tmp2 = VarsUi.FigurePointer; for (Tmp1 = 0;Tmp1 < 3;Tmp1++) { if (Tmp2) { Tmp2--; } else { Tmp2 = StrSets[Type].NoOfFigures - 1; } } for (Tmp1 = 0;Tmp1 < 7;Tmp1++) { if ((Tmp1 == 3) && (StrSets[Type].Figures[Tmp2] == 0x7F)) { pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_1); } else { pMapDisplay->pFunc(DISPLAY_CHAR,TRUE,5 + Tmp1 * 14,52,StrSets[Type].Figures[Tmp2],0); } if (Tmp2 < (StrSets[Type].NoOfFigures - 1)) { Tmp2++; } else { Tmp2 = 0; } } pMapDisplay->pFunc(DISPLAY_HORISONTAL_LINE,TRUE,42,47,57,0); pMapDisplay->pFunc(DISPLAY_VERTICAL_LINE,TRUE,42,47,0,63); pMapDisplay->pFunc(DISPLAY_VERTICAL_LINE,TRUE,57,47,0,63); VarsUi.GUSState++; } } break; case 3 : // Get user input { if ((pMapButton->State[BTN4] & LONG_PRESSED_EV)) { if (VarsUi.GUSCursor) { if ((VarsUi.UserString[VarsUi.GUSCursor - 1] >= 'a') && (VarsUi.UserString[VarsUi.GUSCursor - 1] <= 'z')) { VarsUi.UserString[VarsUi.GUSCursor - 1] -= ' '; VarsUi.GUSState -= 2; } } } switch (cUiReadButtons()) { case BUTTON_LEFT : { if (VarsUi.FigurePointer) { VarsUi.FigurePointer--; } else { VarsUi.FigurePointer = StrSets[Type].NoOfFigures - 1; } pMapDisplay->EraseMask |= BITMAP_BIT(BITMAP_1); VarsUi.GUSState -= 2; } break; case BUTTON_ENTER : { switch (StrSets[Type].Figures[VarsUi.FigurePointer]) { case 0x7F : { VarsUi.GUSState = 100; } break; default : { VarsUi.GUSNoname = FALSE; if (VarsUi.GUSCursor < StrSets[Type].MaxStringLength) { VarsUi.UserString[VarsUi.GUSCursor] = StrSets[Type].Figures[VarsUi.FigurePointer]; VarsUi.GUSCursor++; VarsUi.UserString[VarsUi.GUSCursor] = 0; VarsUi.GUSState -= 2; } } break; } } break; case BUTTON_RIGHT : { if (VarsUi.FigurePointer < (StrSets[Type].NoOfFigures - 1)) { VarsUi.FigurePointer++; } else { VarsUi.FigurePointer = 0; } pMapDisplay->EraseMask |= BITMAP_BIT(BITMAP_1); VarsUi.GUSState -= 2; } break; case BUTTON_EXIT : { if (VarsUi.GUSCursor) { if (VarsUi.GUSNoname == TRUE) { VarsUi.GUSNoname = FALSE; while (VarsUi.GUSCursor) { VarsUi.UserString[VarsUi.GUSCursor] = 0; VarsUi.GUSCursor--; } } else { VarsUi.GUSCursor--; } VarsUi.UserString[VarsUi.GUSCursor] = 0; VarsUi.GUSState -= 2; } else { VarsUi.UserString[0] = 0; VarsUi.GUSState = 100; } } break; } } break; default : // Clean up screen { pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); pMapDisplay->UpdateMask = 0; IOMapUi.Flags |= UI_REDRAW_STATUS; VarsUi.GUSState = 0; } break; } } return (VarsUi.GUSState); } //******* cUiDataLogging ***************************************************** void cUiDrawPortNo(UBYTE *Bitmap,UBYTE MenuIconNo,UBYTE PortNo) { UBYTE Tmp; Bitmap[0] = (UBYTE)(FILEFORMAT_BITMAP >> 8); Bitmap[1] = (UBYTE)(FILEFORMAT_BITMAP); Bitmap[2] = (UBYTE)(SIZE_OF_PORTBITMAP >> 8); Bitmap[3] = (UBYTE)(SIZE_OF_PORTBITMAP); Bitmap[4] = DISPLAY_MENUICONS_X_OFFS + DISPLAY_MENUICONS_X_DIFF * MenuIconNo + 2; Bitmap[5] = DISPLAY_MENUICONS_Y; Bitmap[6] = Port.ItemPixelsX; Bitmap[7] = Port.ItemPixelsY; Tmp = 0; while (Tmp < Bitmap[6]) { Bitmap[Tmp + FILEHEADER_LENGTH] = Port.Data[Tmp + PortNo * Bitmap[6]]; Tmp++; } } UBYTE cUiDataLogging(UBYTE Action) { SBYTE TmpBuffer[DATALOGBUFFERSIZE + 1]; switch (Action) { case MENU_INIT : // Initialize all ports to empty { // Show select pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_VIEW_SELECT); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_SMALL); // Init ports for (VarsUi.Tmp = 0;VarsUi.Tmp < DATALOGPORTS;VarsUi.Tmp++) { VarsUi.DatalogPort[VarsUi.Tmp] = MENU_SENSOR_EMPTY; } } break; case MENU_EXIT : // Initialize all ports to empty { // Show select pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_VIEW_SELECT); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_SMALL); } break; case MENU_TEXT : // Write text { // Init selected sensor and port to none VarsUi.SelectedSensor = MENU_SENSOR_EMPTY; VarsUi.SelectedPort = MENU_PORT_EMPTY; // Count ports VarsUi.Counter = 0; for (VarsUi.Tmp = 0;VarsUi.Tmp < DATALOGPORTS;VarsUi.Tmp++) { if (MENU_SENSOR_EMPTY != VarsUi.DatalogPort[VarsUi.Tmp]) { // Find default port to view if (VarsUi.SelectedPort == MENU_PORT_EMPTY) { VarsUi.SelectedPort = VarsUi.Tmp + MENU_PORT_1; } VarsUi.Counter++; } } if (VarsUi.Counter) { // Display text pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_DATALOGGING_PRESS_EXIT_TO); pMapDisplay->pTextLines[TEXTLINE_4] = cUiGetString(TXT_DATALOGGING_STOP_DATALOGGING); pMapDisplay->TextLinesCenterFlags |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4)); pMapDisplay->UpdateMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4)); } else { cUiMenuPrevFile(); IOMapUi.State = NEXT_MENU; VarsUi.State = 0; } } break; case MENU_RUN : // Run data logging { switch (VarsUi.State) { case 0 : // Init log { // Save menu text VarsUi.MenuIconTextSave = pMapDisplay->pMenuText; // Delete file if exist sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_DATALOG_FILENAME,(char*)TXT_FILE_EXT[FILETYPE_DATALOG]); VarsUi.TmpHandle = pMapLoader->pFunc(FINDFIRST,VarsUi.FilenameBuffer,VarsUi.SearchFilenameBuffer,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); pMapLoader->pFunc(DELETE,VarsUi.FilenameBuffer,NULL,NULL); } // Open file VarsUi.TmpLength = pMapLoader->FreeUserFlash; sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_DATALOG_FILENAME,(char*)TXT_FILE_EXT[FILETYPE_DATALOG]); VarsUi.TmpHandle = pMapLoader->pFunc(OPENWRITEDATA,VarsUi.FilenameBuffer,NULL,&VarsUi.TmpLength); VarsUi.DatalogError = VarsUi.TmpHandle; if (!(VarsUi.DatalogError & 0x8000)) { VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"%s\t%lu",SENSORSYNCDATA,pMapCmd->SyncTime); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\t%lu",pMapCmd->SyncTick); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\t%lu",pMapCmd->Tick); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\t%lu\t-1\r\n",DATALOG_DEFAULT_SAMPLE_TIME); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"%s",SENSORSDATA); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); for (VarsUi.Tmp = 0;(VarsUi.Tmp < DATALOGPORTS) && (!(VarsUi.DatalogError & 0x8000));VarsUi.Tmp++) { if (MENU_SENSOR_EMPTY != VarsUi.DatalogPort[VarsUi.Tmp]) { VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\t%u_%s%s",(UWORD)(VarsUi.Tmp + 1),(char*)SENSORDIRNAME[(VarsUi.DatalogPort[VarsUi.Tmp] - MENU_SENSOR_EMPTY) - 1],(char*)SENSORUNITNAME[(VarsUi.DatalogPort[VarsUi.Tmp] - MENU_SENSOR_EMPTY) - 1]); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); } } VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\r\n"); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"%s",SENSORTIME); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); for (VarsUi.Tmp = 0;(VarsUi.Tmp < DATALOGPORTS) && (!(VarsUi.DatalogError & 0x8000));VarsUi.Tmp++) { if (MENU_SENSOR_EMPTY != VarsUi.DatalogPort[VarsUi.Tmp]) { VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\t%s",(char*)SENSORDIRNAME[(VarsUi.DatalogPort[VarsUi.Tmp] - MENU_SENSOR_EMPTY) - 1]); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); } } VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\r\n"); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); if (!(VarsUi.DatalogError & 0x8000)) { VarsUi.DatalogTimer = 0; VarsUi.DatalogSampleTime = DATALOG_DEFAULT_SAMPLE_TIME; VarsUi.DatalogSampleTimer = 0; VarsUi.Timer = 0; VarsUi.Update = TRUE; IOMapUi.Flags |= UI_BUSY; VarsUi.DatalogOldTick = pMapCmd->Tick; VarsUi.SensorReset = TRUE; VarsUi.State++; } else { pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_SMALL); pMapDisplay->pBitmaps[BITMAP_1] = NULL; VarsUi.State = 4; } } else { // File error pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_SMALL); pMapDisplay->pBitmaps[BITMAP_1] = NULL; VarsUi.State = 3; } } break; case 1 : { // Get real time since last VarsUi.DatalogRTC = (pMapCmd->Tick - VarsUi.DatalogOldTick); VarsUi.DatalogOldTick = pMapCmd->Tick; // Update all timers VarsUi.DatalogTimer += VarsUi.DatalogRTC; VarsUi.DatalogSampleTimer += VarsUi.DatalogRTC; VarsUi.ReadoutTimer += VarsUi.DatalogRTC; // Update sensor values cUiUpdateSensor((SWORD)VarsUi.DatalogRTC); // Check for select change if (VarsUi.Update == TRUE) { VarsUi.Update = FALSE; VarsUi.SelectedSensor = VarsUi.DatalogPort[VarsUi.SelectedPort - MENU_PORT_1]; pMapDisplay->pMenuIcons[MENUICON_CENTER] = cUiMenuGetIconImage(cUiMenuSearchSensorIcon(VarsUi.SelectedSensor)); pMapDisplay->pMenuIcons[MENUICON_LEFT] = NULL; pMapDisplay->pMenuIcons[MENUICON_RIGHT] = NULL; pMapDisplay->EraseMask = SCREEN_BIT(SCREEN_LARGE); pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)&Display; pMapDisplay->UpdateMask = (BITMAP_BIT(BITMAP_1) | MENUICON_BITS | SPECIAL_BIT(TOPLINE) | SPECIAL_BIT(FRAME_SELECT)); pMapDisplay->pBitmaps[BITMAP_2] = (BMPMAP*)VarsUi.PortBitmapLeft; pMapDisplay->pBitmaps[BITMAP_3] = (BMPMAP*)VarsUi.PortBitmapCenter; pMapDisplay->pBitmaps[BITMAP_4] = (BMPMAP*)VarsUi.PortBitmapRight; cUiDrawPortNo(VarsUi.PortBitmapCenter,MENUICON_CENTER,VarsUi.SelectedPort - MENU_PORT_EMPTY); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_3); if (VarsUi.Counter == 2) { VarsUi.Tmp = VarsUi.SelectedPort; do { VarsUi.Tmp++; if (VarsUi.Tmp >= MENU_PORT_INVALID) { VarsUi.Tmp = MENU_PORT_1; } } while (VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1] == MENU_SENSOR_EMPTY); if (VarsUi.Tmp > VarsUi.SelectedPort) { pMapDisplay->pMenuIcons[MENUICON_RIGHT] = cUiMenuGetIconImage(cUiMenuSearchSensorIcon(VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1])); cUiDrawPortNo(VarsUi.PortBitmapRight,MENUICON_RIGHT,VarsUi.Tmp - MENU_PORT_EMPTY); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_4); } else { pMapDisplay->pMenuIcons[MENUICON_LEFT] = cUiMenuGetIconImage(cUiMenuSearchSensorIcon(VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1])); cUiDrawPortNo(VarsUi.PortBitmapLeft,MENUICON_LEFT,VarsUi.Tmp - MENU_PORT_EMPTY); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_2); } } if (VarsUi.Counter > 2) { VarsUi.Tmp = VarsUi.SelectedPort; do { VarsUi.Tmp++; if (VarsUi.Tmp >= MENU_PORT_INVALID) { VarsUi.Tmp = MENU_PORT_1; } } while (VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1] == MENU_SENSOR_EMPTY); pMapDisplay->pMenuIcons[MENUICON_RIGHT] = cUiMenuGetIconImage(cUiMenuSearchSensorIcon(VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1])); cUiDrawPortNo(VarsUi.PortBitmapRight,MENUICON_RIGHT,VarsUi.Tmp - MENU_PORT_EMPTY); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_4); VarsUi.Tmp = VarsUi.SelectedPort; do { VarsUi.Tmp--; if (VarsUi.Tmp <= MENU_PORT_EMPTY) { VarsUi.Tmp = MENU_PORT_INVALID - 1; } } while (VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1] == MENU_SENSOR_EMPTY); pMapDisplay->pMenuIcons[MENUICON_LEFT] = cUiMenuGetIconImage(cUiMenuSearchSensorIcon(VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1])); cUiDrawPortNo(VarsUi.PortBitmapLeft,MENUICON_LEFT,VarsUi.Tmp - MENU_PORT_EMPTY); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_2); } VarsUi.ReadoutTimer = DISPLAY_VIEW_UPDATE; } // Write sample if timeout if (VarsUi.DatalogSampleTimer >= VarsUi.DatalogSampleTime) { VarsUi.DatalogSampleTimer -= VarsUi.DatalogSampleTime; VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"%lu",VarsUi.DatalogTimer - VarsUi.DatalogSampleTime); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); for (VarsUi.Tmp = 0;(VarsUi.Tmp < DATALOGPORTS) && (!(VarsUi.DatalogError & 0x8000));VarsUi.Tmp++) { if (MENU_SENSOR_EMPTY != VarsUi.DatalogPort[VarsUi.Tmp]) { if (VarsUi.DatalogSampleValid[VarsUi.Tmp] == TRUE) { VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,(char*)SENSORFORMAT2[(VarsUi.DatalogPort[VarsUi.Tmp] - MENU_SENSOR_EMPTY) - 1],(float)VarsUi.DatalogSampleValue[VarsUi.Tmp] / SENSORDIVIDER[VarsUi.DatalogPort[VarsUi.Tmp] - MENU_SENSOR_EMPTY]); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); } else { VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\t-"); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); } } } VarsUi.TmpLength = (ULONG)sprintf((char*)TmpBuffer,"\r\n"); VarsUi.DatalogError |= pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)TmpBuffer,&VarsUi.TmpLength); } // Refresh display if (++VarsUi.ReadoutTimer >= DISPLAY_VIEW_UPDATE) { VarsUi.ReadoutTimer = 0; // Display sensor value cUiPrintSensorInDisplayBuffer(VarsUi.SelectedPort); pMapDisplay->pTextLines[TEXTLINE_4] = VarsUi.DisplayBuffer; pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_4); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_4); } // Test for file error if ((VarsUi.DatalogError & 0x8000)) { pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_SMALL); pMapDisplay->pBitmaps[BITMAP_1] = NULL; VarsUi.State = 4; } // Test for break; switch (cUiReadButtons()) { case BUTTON_EXIT : { VarsUi.State++; } break; case BUTTON_LEFT : { VarsUi.Tmp = VarsUi.SelectedPort; do { VarsUi.Tmp--; if (VarsUi.Tmp <= MENU_PORT_EMPTY) { VarsUi.Tmp = MENU_PORT_INVALID - 1; } } while (VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1] == MENU_SENSOR_EMPTY); if ((VarsUi.Counter > 2) || (VarsUi.Tmp < VarsUi.SelectedPort)) { VarsUi.SelectedPort = VarsUi.Tmp; } VarsUi.Update = TRUE; } break; case BUTTON_RIGHT : { VarsUi.Tmp = VarsUi.SelectedPort; do { VarsUi.Tmp++; if (VarsUi.Tmp >= MENU_PORT_INVALID) { VarsUi.Tmp = MENU_PORT_1; } } while (VarsUi.DatalogPort[VarsUi.Tmp - MENU_PORT_1] == MENU_SENSOR_EMPTY); if ((VarsUi.Counter > 2) || (VarsUi.Tmp > VarsUi.SelectedPort)) { VarsUi.SelectedPort = VarsUi.Tmp; } VarsUi.Update = TRUE; } break; } IOMapUi.Flags |= UI_RESET_SLEEP_TIMER; } break; case 2 : { // Close file pMapLoader->pFunc(CROPDATAFILE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); // Clean up pMapDisplay->pMenuText = VarsUi.MenuIconTextSave; cUiReleaseSensors(); IOMapUi.Flags &= ~UI_BUSY; IOMapUi.State = RIGHT_PRESSED; VarsUi.State = 0; } break; case 3 : // Display memory full text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_DL_ERROR_MEMORY_FULL_1,TXT_FB_DL_ERROR_MEMORY_FULL_2,DISPLAY_SHOW_ERROR_TIME)) { cUiMenuPrevFile(); IOMapUi.State = NEXT_MENU; VarsUi.State = 0; } } break; case 4 : // Display memory full text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_DL_ERROR_MEMORY_FULL_1,TXT_FB_DL_ERROR_MEMORY_FULL_2,DISPLAY_SHOW_ERROR_TIME)) { VarsUi.State = 2; } } break; } } break; case MENU_SAVE : // Save datalog file { switch (VarsUi.State) { case 0 : { VarsUi.NVData.DatalogNumber++; if (VarsUi.NVData.DatalogNumber > MAX_DATALOGS) { VarsUi.NVData.DatalogNumber = 1; } cUiNVWrite(); sprintf((char*)VarsUi.SelectedFilename,"%s%u.%s",(char*)UI_DATALOG_FILENAME,VarsUi.NVData.DatalogNumber,TXT_FILE_EXT[FILETYPE_DATALOG]); VarsUi.State++; } break; case 1 : { // Rename TEMP_DATALOG_FILENAME to VarsUi.SelectedFilename(user filename) sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_DATALOG_FILENAME,(char*)TXT_FILE_EXT[FILETYPE_DATALOG]); VarsUi.TmpHandle = pMapLoader->pFunc(RENAMEFILE,VarsUi.FilenameBuffer,VarsUi.SelectedFilename,&VarsUi.TmpLength); VarsUi.State++; } break; case 2 : // Display saved text { if (!cUiFeedback((BMPMAP*)&Info,TXT_FB_DL_FILE_SAVED_INFO,0xFF,DISPLAY_SHOW_FILENAME_TIME)) { VarsUi.State++; } } break; default : { cUiMenuPrevFile(); IOMapUi.State = NEXT_MENU; VarsUi.State = 0; } break; } } break; case MENU_DELETE : // Delete datalog file { switch (VarsUi.State) { case 0 : { // Delete file if exist sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_DATALOG_FILENAME,(char*)TXT_FILE_EXT[FILETYPE_DATALOG]); pMapLoader->pFunc(DELETE,VarsUi.FilenameBuffer,NULL,NULL); VarsUi.State++; } break; case 1 : { pMapDisplay->EraseMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4) | TEXTLINE_BIT(TEXTLINE_5) | MENUICON_BITS | SPECIAL_BIT(MENUTEXT)); VarsUi.Timer = DISPLAY_SHOW_TIME; VarsUi.State++; } break; case 2 : { if (++VarsUi.Timer >= DISPLAY_SHOW_TIME) { pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_3); VarsUi.State++; } } break; default : { VarsUi.State = 0; } break; } } break; case MENU_SELECT : // Save sensor { pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_VIEW_SELECT); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); VarsUi.DatalogPort[VarsUi.SelectedPort - MENU_PORT_1] = VarsUi.SelectedSensor; IOMapUi.State = EXIT_PRESSED; } break; default : { switch (VarsUi.State) { case 0 : { if ((Action > MENU_SENSOR_EMPTY) && (Action < MENU_SENSOR_INVALID)) { VarsUi.SelectedSensor = Action; } if ((Action > MENU_PORT_EMPTY) && (Action < MENU_PORT_INVALID)) { VarsUi.SelectedPort = Action; if (VarsUi.DatalogPort[VarsUi.SelectedPort - MENU_PORT_1] != MENU_SENSOR_EMPTY) { // Port occupied pMapDisplay->pTextLines[TEXTLINE_4] = cUiGetString(TXT_DATALOGGING_PORT_OCCUPIED); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_4); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_4); VarsUi.Timer = 0; VarsUi.State++; } } } break; default : { if ((++VarsUi.Timer >= DISPLAY_SHOW_TIME) || (BUTTON_NONE != cUiReadButtons())) { pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_4); cUiMenuPrev(); IOMapUi.State = NEXT_MENU; VarsUi.State = 0; } } break; } } break; } return (VarsUi.State); } //******* cUiRunning ********************************************************** void cUiRunning(UBYTE Action) { switch (Action) { case MENU_INIT : { VarsUi.RunIconSave = pMapDisplay->pMenuIcons[MENUICON_CENTER]; VarsUi.RunBitmapPointer = 0; VarsUi.RunTimer = 0; pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_LARGE); pMapDisplay->UpdateMask |= SPECIAL_BIT(TOPLINE); } break; case MENU_RUN : { if ((IOMapUi.Flags & UI_ENABLE_STATUS_UPDATE)) { if (++VarsUi.RunTimer >= RUN_BITMAP_CHANGE_TIME) { VarsUi.RunTimer = 0; if (++VarsUi.RunBitmapPointer >= Running.ItemsY ) { VarsUi.RunBitmapPointer = 0; } pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Running.Data[VarsUi.RunBitmapPointer * Running.ItemPixelsX * (Running.ItemPixelsY / 8)]; pMapDisplay->EraseMask |= MENUICON_BIT(MENUICON_CENTER); pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_CENTER); } } } break; case MENU_UPDATE : { pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Running.Data[VarsUi.RunBitmapPointer * Running.ItemPixelsX * (Running.ItemPixelsY / 8)]; pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_CENTER); } break; case MENU_EXIT : { pMapDisplay->pMenuIcons[MENUICON_CENTER] = VarsUi.RunIconSave; pMapDisplay->UpdateMask = MENUICON_BITS | SPECIAL_BIT(MENUTEXT); } break; } } //******* cUiOnBrickProgramming ********************************************** UBYTE cUiOnBrickProgramming(UBYTE Action) // On brick programming { switch (Action) { case MENU_INIT : // Show motor / sensor text { pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_ONBRICKPROGRAMMING_PLEASE_USE_PORT); pMapDisplay->pTextLines[TEXTLINE_4] = cUiGetString(TXT_ONBRICKPROGRAMMING_1_TOUCH_SENSOR); pMapDisplay->pTextLines[TEXTLINE_5] = cUiGetString(TXT_ONBRICKPROGRAMMING_2_SOUND_SENSOR); pMapDisplay->pTextLines[TEXTLINE_6] = cUiGetString(TXT_ONBRICKPROGRAMMING_3_LIGHT_SENSOR); pMapDisplay->pTextLines[TEXTLINE_7] = cUiGetString(TXT_ONBRICKPROGRAMMING_4_ULTRA_SONIC); pMapDisplay->pTextLines[TEXTLINE_8] = cUiGetString(TXT_ONBRICKPROGRAMMING_BC_LR_MOTORS); pMapDisplay->EraseMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4) | TEXTLINE_BIT(TEXTLINE_5) | TEXTLINE_BIT(TEXTLINE_6) | TEXTLINE_BIT(TEXTLINE_7) | TEXTLINE_BIT(TEXTLINE_8)); pMapDisplay->UpdateMask &= ~SPECIAL_BIT(FRAME_SELECT); pMapDisplay->UpdateMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4) | TEXTLINE_BIT(TEXTLINE_5) | TEXTLINE_BIT(TEXTLINE_6) | TEXTLINE_BIT(TEXTLINE_7) | TEXTLINE_BIT(TEXTLINE_8) | SPECIAL_BIT(TOPLINE)); pMapDisplay->TextLinesCenterFlags |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4) | TEXTLINE_BIT(TEXTLINE_5) | TEXTLINE_BIT(TEXTLINE_6) | TEXTLINE_BIT(TEXTLINE_7) | TEXTLINE_BIT(TEXTLINE_8)); IOMapUi.State = TEST_BUTTONS; } break; case MENU_TEXT : // Show empty program steps { pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_LARGE); VarsUi.pTmp = (UBYTE*)&Cursor; for (VarsUi.Tmp = 0;(VarsUi.Tmp < SIZE_OF_CURSOR) && (VarsUi.Tmp < Cursor_size);VarsUi.Tmp++) { VarsUi.CursorTmp[VarsUi.Tmp] = *VarsUi.pTmp; VarsUi.pTmp++; } for (VarsUi.ProgramStepPointer = 0;VarsUi.ProgramStepPointer < ON_BRICK_PROGRAMSTEPS;VarsUi.ProgramStepPointer++) { VarsUi.ProgramSteps[VarsUi.ProgramStepPointer] = MENU_ACTION_EMPTY; } VarsUi.ProgramStepPointer = 0; Action = MENU_DRAW; } break; case MENU_EXIT : // Delete one steps and exit at the end { if (VarsUi.ProgramStepPointer) { if (VarsUi.ProgramStepPointer < ON_BRICK_PROGRAMSTEPS) { VarsUi.ProgramSteps[VarsUi.ProgramStepPointer] = MENU_ACTION_EMPTY; } VarsUi.ProgramStepPointer--; } else { IOMapUi.State = NEXT_MENU; } Action = MENU_DRAW; } break; case MENU_RUN : // Run program steps until end or user press exit button { switch (VarsUi.State) { case 0 : { VarsUi.pTmp = (UBYTE*)&Cursor; for (VarsUi.Tmp = 0;(VarsUi.Tmp < SIZE_OF_CURSOR) && (VarsUi.Tmp < Cursor_size);VarsUi.Tmp++) { VarsUi.CursorTmp[VarsUi.Tmp] = *VarsUi.pTmp; VarsUi.pTmp++; } pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)VarsUi.CursorTmp; cUiRunning(MENU_INIT); Action = MENU_DRAW; VarsUi.State++; } break; case 1 : // If sound finished -> Init text and program pointer { if (SOUND_IDLE == pMapSound->State) { VarsUi.ProgramStepPointer = ON_BRICK_PROGRAMSTEPS; VarsUi.MenuIconTextSave = pMapDisplay->pMenuText; pMapDisplay->EraseMask |= SPECIAL_BIT(MENUTEXT); VarsUi.State++; } } break; case 2 : // load file to run { if (PROG_IDLE == pMapCmd->ProgStatus) { sprintf((char*)pMapCmd->FileName,"%s.%s",(char*)VM_PROGRAM_READER,(char*)TXT_SYS_EXT); pMapCmd->ActivateFlag = TRUE; VarsUi.State++; } } break; case 3 : // Wait for end of file { if (PROG_RUNNING != pMapCmd->ProgStatus) { pMapCmd->ProgStatus = PROG_RESET; VarsUi.State = 99; VarsUi.ProgramStepPointer = ON_BRICK_PROGRAMSTEPS; } else { if (VarsUi.OBPTimer >= MIN_DISPLAY_UPDATE_TIME) { if (IOMapUi.OBPPointer != VarsUi.ProgramStepPointer) { VarsUi.ProgramStepPointer = IOMapUi.OBPPointer; Action = MENU_DRAW; } } } } break; default : // Program stopped { pMapDisplay->pMenuText = VarsUi.MenuIconTextSave; pMapDisplay->UpdateMask |= SPECIAL_BIT(MENUTEXT); Action = MENU_DRAW; VarsUi.State = 0; } break; } if (VarsUi.State) { cUiRunning(MENU_RUN); } else { cUiRunning(MENU_EXIT); } } break; case MENU_LEFT : // NA { IOMapUi.State = TEST_BUTTONS; } break; case MENU_RIGHT : // NA { IOMapUi.State = TEST_BUTTONS; } break; case MENU_UPDATE : // NA { Action = MENU_DRAW; } break; case MENU_SAVE : // Save NXT program { switch (VarsUi.State) { case 0 : { // Suggest default filename to user strcpy((char*)VarsUi.UserString,(char*)DEFAULT_PROGRAM_NAME); VarsUi.State++; } break; case 1 : { if (!cUiGetUserString(1)) { if (VarsUi.UserString[0]) { sprintf((char*)VarsUi.SelectedFilename,"%s.%s",VarsUi.UserString,TXT_FILE_EXT[FILETYPE_NXT]); // If tmp file exist -> ask for overwrite VarsUi.TmpHandle = pMapLoader->pFunc(FINDFIRST,(UBYTE*)VarsUi.SelectedFilename,VarsUi.FilenameBuffer,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); VarsUi.State++; } else { VarsUi.State += 2; } } else { VarsUi.State = 99; } } } break; case 2 : { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_OBP_FILE_EXIST_FAIL,TXT_FB_OBP_OVERWRITE_FAIL,0)) { VarsUi.State = 0; } } break; case 3 : { // Rename TEMP_PROGRAM_FILENAME to VarsUi.SelectedFilename(user filename) sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_PROGRAM_FILENAME,(char*)TXT_TMP_EXT); VarsUi.TmpHandle = pMapLoader->pFunc(RENAMEFILE,VarsUi.FilenameBuffer,VarsUi.SelectedFilename,&VarsUi.TmpLength); pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); VarsUi.State++; } break; case 4 : // Display saved text { if (!cUiFeedback((BMPMAP*)&Info,TXT_FB_OBP_FILE_SAVED_INFO,0,DISPLAY_SHOW_TIME)) { VarsUi.State++; } } break; default : { cUiMenuPrevFile(); IOMapUi.State = NEXT_MENU; VarsUi.State = 0; } break; } } break; case MENU_OVERWRITE : // Over write existing file { switch (VarsUi.State) { case 0 : { // Delete VarsUi.SelectedFilename(user filename) VarsUi.TmpHandle = pMapLoader->pFunc(FINDFIRST,(UBYTE*)VarsUi.SelectedFilename,VarsUi.FilenameBuffer,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); pMapLoader->pFunc(DELETE,VarsUi.SelectedFilename,NULL,NULL); } // Rename TEMP_PROGRAM_FILENAME to VarsUi.SelectedFilename(user filename) sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_PROGRAM_FILENAME,(char*)TXT_TMP_EXT); VarsUi.TmpHandle = pMapLoader->pFunc(RENAMEFILE,VarsUi.FilenameBuffer,VarsUi.SelectedFilename,&VarsUi.TmpLength); pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); VarsUi.State++; } break; default : // Display saved text { if (!cUiFeedback((BMPMAP*)&Info,TXT_FB_OBP_FILE_SAVED_INFO,0,DISPLAY_SHOW_TIME)) { VarsUi.State = 0; } } break; } } break; default : // Insert selected action/waitfor in program and save if finished { switch (VarsUi.State) { case 0 : { VarsUi.ProgramSteps[VarsUi.ProgramStepPointer] = Action; if (VarsUi.ProgramStepPointer < ON_BRICK_PROGRAMSTEPS) { VarsUi.ProgramStepPointer++; } if (VarsUi.ProgramStepPointer == ON_BRICK_PROGRAMSTEPS) { // If tmp file exist -> delete it sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_PROGRAM_FILENAME,(char*)TXT_TMP_EXT); VarsUi.TmpHandle = pMapLoader->pFunc(FINDFIRST,VarsUi.FilenameBuffer,VarsUi.SearchFilenameBuffer,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); pMapLoader->pFunc(DELETE,VarsUi.FilenameBuffer,NULL,NULL); } // Save program as tmp file VarsUi.TmpLength = FILEHEADER_LENGTH + ON_BRICK_PROGRAMSTEPS; VarsUi.TmpHandle = pMapLoader->pFunc(OPENWRITE,VarsUi.FilenameBuffer,NULL,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { VarsUi.FileHeader[0] = (UBYTE)(FILEFORMAT_PROGRAM >> 8); VarsUi.FileHeader[1] = (UBYTE)(FILEFORMAT_PROGRAM); VarsUi.FileHeader[2] = (UBYTE)(ON_BRICK_PROGRAMSTEPS >> 8); VarsUi.FileHeader[3] = (UBYTE)(ON_BRICK_PROGRAMSTEPS); VarsUi.FileHeader[4] = (UBYTE)(ON_BRICK_PROGRAMSTEPS); VarsUi.FileHeader[5] = (UBYTE)0; VarsUi.FileHeader[6] = (UBYTE)0; VarsUi.FileHeader[7] = (UBYTE)0; VarsUi.TmpLength = FILEHEADER_LENGTH; pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)VarsUi.FileHeader,&VarsUi.TmpLength); VarsUi.TmpLength = ON_BRICK_PROGRAMSTEPS; pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)VarsUi.ProgramSteps,&VarsUi.TmpLength); pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); } else { VarsUi.State++; } } Action = MENU_DRAW; } break; default : // Display memory error text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_OBP_MEMORY_FULL_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { cUiMenuPrevFile(); IOMapUi.State = NEXT_MENU; VarsUi.State = 0; } } break; } } break; } // Update display screen if (Action == MENU_DRAW) { VarsUi.OBPTimer = 0; for (VarsUi.Pointer = 0;VarsUi.Pointer < ON_BRICK_PROGRAMSTEPS;VarsUi.Pointer++) { VarsUi.Tmp = VarsUi.ProgramSteps[VarsUi.Pointer]; if ((VarsUi.Tmp >= MENU_ACTION_EMPTY) && (VarsUi.Tmp < MENU_ACTION_INVALID)) { VarsUi.Tmp -= MENU_ACTION_EMPTY; pMapDisplay->StepIcons[VarsUi.Pointer] = VarsUi.Tmp + 1; } if ((VarsUi.Tmp >= MENU_WAIT_EMPTY) && (VarsUi.Tmp < MENU_WAIT_INVALID)) { VarsUi.Tmp -= MENU_WAIT_EMPTY; pMapDisplay->StepIcons[VarsUi.Pointer] = VarsUi.Tmp + 1 + 16; } if (VarsUi.Tmp == MENU_LOOP) { pMapDisplay->StepIcons[VarsUi.Pointer] = 31; } if (VarsUi.Tmp == MENU_STOP) { pMapDisplay->StepIcons[VarsUi.Pointer] = 32; } pMapDisplay->UpdateMask |= STEPICON_BIT(STEPICON_1 + VarsUi.Pointer); } // and cursor pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)VarsUi.CursorTmp; if (VarsUi.ProgramStepPointer < ON_BRICK_PROGRAMSTEPS) { VarsUi.CursorTmp[4] = 13 + (VarsUi.ProgramStepPointer * 17); VarsUi.CursorTmp[5] = 24; pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_1); } if (PROG_RUNNING != pMapCmd->ProgStatus) { pMapDisplay->EraseMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_2)); } pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_4); pMapDisplay->UpdateMask |= (SPECIAL_BIT(STEPLINE) | SPECIAL_BIT(TOPLINE)); } return (VarsUi.State); } //******* cUiFileRun ********************************************************** UBYTE cUiFindFileType(UBYTE *Filename) // Find file type number { UBYTE Ext[FILENAME_LENGTH + 1]; UBYTE Result; UBYTE Tmp1; UBYTE Tmp2; Result = FILETYPE_ALL; Tmp1 = 0; while ((Filename[Tmp1]) && (Tmp1 < FILENAME_LENGTH)) // Search forward for termination { Tmp1++; } while ((Tmp1) && (Filename[Tmp1] != '.')) // Search backward for "." { Tmp1--; } if (Filename[Tmp1] == '.') // If "." { Tmp1++; Tmp2 = 0; while ((Filename[Tmp1]) && (Tmp1 < FILENAME_LENGTH)) // Convert to upper to Ext { Ext[Tmp2] = tolower(Filename[Tmp1]); Tmp1++; Tmp2++; } Ext[Tmp2] = 0; // Inser termination // Calculate type for (Tmp1 = FILETYPE_ALL;(Tmp1 < FILETYPES) && (Result == FILETYPE_ALL);Tmp1++) { if (strcmp((char*)TXT_FILE_EXT[Tmp1],(char*)Ext) == 0) { Result = Tmp1; } } } return (Result); } #define FILERUN_FILENAMELINE TEXTLINE_4 #define FILERUN_TEXTLINE TEXTLINE_5 UBYTE cUiFileRun(UBYTE Action) // Run selected file { switch (Action) { case MENU_INIT : { VarsUi.Tmp = 0; while ((VarsUi.SelectedFilename[VarsUi.Tmp]) && (VarsUi.Tmp < FILENAME_LENGTH)) // Search forward for termination { VarsUi.Tmp++; } while ((VarsUi.Tmp) && (VarsUi.SelectedFilename[VarsUi.Tmp] != '.')) // Search backward for "." { VarsUi.Tmp--; } if (VarsUi.Tmp > DISPLAYLINE_LENGTH) { VarsUi.Tmp = DISPLAYLINE_LENGTH; } VarsUi.DisplayBuffer[VarsUi.Tmp] = 0; while (VarsUi.Tmp) // Copy only name not ext { VarsUi.Tmp--; VarsUi.DisplayBuffer[VarsUi.Tmp] = VarsUi.SelectedFilename[VarsUi.Tmp]; } pMapDisplay->pTextLines[FILERUN_FILENAMELINE] = (UBYTE*)VarsUi.DisplayBuffer; pMapDisplay->TextLinesCenterFlags = TEXTLINE_BIT(FILERUN_FILENAMELINE); pMapDisplay->UpdateMask = TEXTLINE_BIT(FILERUN_FILENAMELINE); } break; case MENU_RUN : { if (VarsUi.Timer < DISPLAY_SHOW_TIME) { VarsUi.Timer++; } switch (VarsUi.State) { case 0 : { IOMapUi.Flags |= UI_BUSY; VarsUi.State++; } break; case 1 : // Set state from extention when sound is ready { if (SOUND_IDLE == pMapSound->State) { pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_RUNNING); pMapDisplay->UpdateMask = (TEXTLINE_BIT(FILERUN_TEXTLINE) | TEXTLINE_BIT(FILERUN_FILENAMELINE)); pMapDisplay->TextLinesCenterFlags = (TEXTLINE_BIT(FILERUN_TEXTLINE) | TEXTLINE_BIT(FILERUN_FILENAMELINE)); cUiRunning(MENU_INIT); VarsUi.State++; } } break; case 2 : { if ((!pMapDisplay->EraseMask) && (!pMapDisplay->UpdateMask)) { VarsUi.State = 10 * cUiFindFileType(VarsUi.SelectedFilename); if (VarsUi.State == (FILETYPE_TRYME * 10)) { VarsUi.State = FILETYPE_LMS * 10; } } } break; case (FILETYPE_SOUND * 10 + 0) : // Start sound file (*.snd, *.rso) Wait for sound idle { strcpy((char*)pMapSound->SoundFilename,(char*)VarsUi.SelectedFilename); pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_ONCE; pMapSound->Flags |= SOUND_UPDATE; VarsUi.State++; } break; case (FILETYPE_SOUND * 10 + 1) : // Wait for stop or user break { cUiRunning(MENU_RUN); if (SOUND_IDLE == pMapSound->State) { pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_ENDED); VarsUi.State = 99; } if (BUTTON_EXIT == cUiReadButtons()) { pMapSound->Flags &= ~SOUND_UPDATE; pMapSound->State = SOUND_STOP; pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_ABORTED); VarsUi.State = 99; } } break; case (FILETYPE_LMS * 10 + 0) : // Start LMS file (*.rxe) { if ((!pMapDisplay->EraseMask) && (pMapCmd->ProgStatus == PROG_IDLE) && (!pMapButton->State[BTN4])) { strcpy((char*)pMapCmd->FileName,(char*)VarsUi.SelectedFilename); pMapCmd->ActivateFlag = TRUE; VarsUi.State++; } } break; case (FILETYPE_LMS * 10 + 1) : // Wait for program stop or user break { cUiRunning(MENU_RUN); if ((IOMapUi.Flags & UI_REDRAW_STATUS) && (IOMapUi.Flags & UI_ENABLE_STATUS_UPDATE)) { pMapDisplay->pTextLines[FILERUN_FILENAMELINE] = (UBYTE*)VarsUi.DisplayBuffer; pMapDisplay->TextLinesCenterFlags = TEXTLINE_BIT(FILERUN_FILENAMELINE); pMapDisplay->UpdateMask = TEXTLINE_BIT(FILERUN_FILENAMELINE); pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_RUNNING); pMapDisplay->UpdateMask = (TEXTLINE_BIT(FILERUN_TEXTLINE) | TEXTLINE_BIT(FILERUN_FILENAMELINE)); pMapDisplay->TextLinesCenterFlags = (TEXTLINE_BIT(FILERUN_TEXTLINE) | TEXTLINE_BIT(FILERUN_FILENAMELINE)); } switch (pMapCmd->ProgStatus) { case PROG_RUNNING : { } break; case PROG_OK : { pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_ENDED); VarsUi.State = 99; } break; case PROG_ABORT : { pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_ABORTED); VarsUi.State = 99; } break; default : { pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_FILE_ERROR); VarsUi.State = 99; } break; } } break; case (FILETYPE_NXT * 10 + 0) :// Start Program file (*.prg) { VarsUi.TmpHandle = pMapLoader->pFunc(OPENREAD,VarsUi.SelectedFilename,NULL,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { VarsUi.TmpLength = FILEHEADER_LENGTH; pMapLoader->pFunc(READ,(UBYTE*)&VarsUi.TmpHandle,VarsUi.FileHeader,&VarsUi.TmpLength); VarsUi.TmpLength = ON_BRICK_PROGRAMSTEPS; pMapLoader->pFunc(READ,(UBYTE*)&VarsUi.TmpHandle,VarsUi.ProgramSteps,&VarsUi.TmpLength); pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); } if ((ON_BRICK_PROGRAMSTEPS == VarsUi.TmpLength) && (VarsUi.FileHeader[0] == (UBYTE)(FILEFORMAT_PROGRAM >> 8)) && (VarsUi.FileHeader[1] == (UBYTE)(FILEFORMAT_PROGRAM))) { // If tmp file exist -> delete it sprintf((char*)VarsUi.FilenameBuffer,"%s.%s",(char*)TEMP_PROGRAM_FILENAME,(char*)TXT_TMP_EXT); VarsUi.TmpHandle = pMapLoader->pFunc(FINDFIRST,VarsUi.FilenameBuffer,VarsUi.SearchFilenameBuffer,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); pMapLoader->pFunc(DELETE,VarsUi.FilenameBuffer,NULL,NULL); } // Save program as tmp file VarsUi.TmpLength = FILEHEADER_LENGTH + ON_BRICK_PROGRAMSTEPS; VarsUi.TmpHandle = pMapLoader->pFunc(OPENWRITE,VarsUi.FilenameBuffer,NULL,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { VarsUi.TmpLength = FILEHEADER_LENGTH; pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)VarsUi.FileHeader,&VarsUi.TmpLength); VarsUi.TmpLength = ON_BRICK_PROGRAMSTEPS; pMapLoader->pFunc(WRITE,(UBYTE*)&VarsUi.TmpHandle,(UBYTE*)VarsUi.ProgramSteps,&VarsUi.TmpLength); pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); } pMapDisplay->UpdateMask &= ~TEXTLINE_BIT(FILERUN_FILENAMELINE); pMapDisplay->EraseMask |= TEXTLINE_BIT(FILERUN_FILENAMELINE); VarsUi.State++; } else { pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_FILE_ERROR); VarsUi.State = 99; } VarsUi.GUSState = 0; } break; case (FILETYPE_NXT * 10 + 1) : // Wait for program stop or user break { VarsUi.State = VarsUi.GUSState; cUiOnBrickProgramming(MENU_RUN); VarsUi.GUSState = VarsUi.State; if (VarsUi.State) { VarsUi.State = (FILETYPE_NXT * 10 + 1); } else { pMapDisplay->pTextLines[FILERUN_TEXTLINE] = cUiGetString(TXT_FILERUN_ENDED); VarsUi.State = 99; } } break; case 99 : // Wait for display show time or user action { pMapDisplay->EraseMask = SCREEN_BIT(SCREEN_LARGE); pMapDisplay->UpdateMask = (TEXTLINE_BIT(FILERUN_TEXTLINE) | TEXTLINE_BIT(FILERUN_FILENAMELINE)); pMapDisplay->TextLinesCenterFlags = (TEXTLINE_BIT(FILERUN_TEXTLINE) | TEXTLINE_BIT(FILERUN_FILENAMELINE)); IOMapUi.Flags |= UI_REDRAW_STATUS | UI_ENABLE_STATUS_UPDATE; cUiRunning(MENU_UPDATE); VarsUi.Timer = 0; VarsUi.State++; } break; default : { if ((++VarsUi.Timer >= DISPLAY_SHOW_TIME) || (BUTTON_NONE != cUiReadButtons())) { if (pMapCmd->ProgStatus != PROG_IDLE) pMapCmd->ProgStatus = PROG_RESET; pMapDisplay->UpdateMask = 0; pMapDisplay->TextLinesCenterFlags = 0; cUiRunning(MENU_EXIT); pMapDisplay->EraseMask = TEXTLINE_BIT(FILERUN_TEXTLINE); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(FILERUN_FILENAMELINE); pMapDisplay->UpdateMask |= TEXTLINE_BIT(FILERUN_FILENAMELINE); IOMapUi.Flags &= ~UI_BUSY; VarsUi.State = 0; } } break; } } break; } return (VarsUi.State); } //******* cUiFileDelete ******************************************************* UBYTE cUiFileDelete(UBYTE Action) { if (MENU_INIT == Action) { switch (VarsUi.State) { case 0 : { VarsUi.State++; } break; case 1 : { if (SOUND_IDLE == pMapSound->State) { VarsUi.State++; } } break; case 2 : { pMapLoader->pFunc(DELETE,VarsUi.SelectedFilename,NULL,NULL); VarsUi.State++; } break; default : // Display deleted text { if (!cUiFeedback((BMPMAP*)&Info,TXT_FB_FD_FILE_DELETED_INFO,0,DISPLAY_SHOW_TIME)) { IOMapUi.State = EXIT_PRESSED; VarsUi.State = 0; } } break; } } return (VarsUi.State); } //******* cUiView ************************************************************ UBYTE cUiView(UBYTE Action) // MENU_INIT { switch (VarsUi.State) { case 0 : { switch (Action) { case MENU_INIT : // Init { pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_VIEW_SELECT); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_SMALL); // Init ports for (VarsUi.Tmp = 0;VarsUi.Tmp < DATALOGPORTS;VarsUi.Tmp++) { VarsUi.DatalogPort[VarsUi.Tmp] = MENU_SENSOR_EMPTY; } } break; default : { if ((Action > MENU_SENSOR_EMPTY) && (Action < MENU_SENSOR_INVALID)) { VarsUi.SelectedSensor = Action; } if ((Action >= MENU_PORT_1) && (Action <= MENU_PORT_C)) { VarsUi.SelectedPort = Action; VarsUi.DatalogPort[VarsUi.SelectedPort - MENU_PORT_1] = VarsUi.SelectedSensor; IOMapUi.Flags |= UI_BUSY; pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_LARGE); pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)&Display; pMapDisplay->UpdateMask = BITMAP_BIT(BITMAP_1); IOMapUi.Flags |= UI_REDRAW_STATUS; VarsUi.ReadoutTimer = 0;; VarsUi.State++; VarsUi.SensorReset = TRUE; } } break; } } break; case 1 : { VarsUi.ReadoutTimer++; cUiUpdateSensor(1); if (VarsUi.ReadoutTimer >= DISPLAY_VIEW_UPDATE) { VarsUi.ReadoutTimer = 0; cUiPrintSensorInDisplayBuffer(VarsUi.SelectedPort); pMapDisplay->pTextLines[TEXTLINE_4] = VarsUi.DisplayBuffer; pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_4); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_4); } VarsUi.Tmp = cUiReadButtons(); if (VarsUi.Tmp == BUTTON_EXIT) { pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_VIEW_SELECT); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask &= ~TEXTLINE_BIT(TEXTLINE_4); pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_SMALL); VarsUi.State++; } if (VarsUi.Tmp == BUTTON_ENTER) { VarsUi.SensorReset = TRUE; } } break; default : { cUiReleaseSensors(); IOMapUi.Flags &= ~UI_BUSY; VarsUi.State = 0; IOMapUi.State = EXIT_PRESSED; } break; } return (VarsUi.State); } //******* cUiBtOn ************************************************************ UBYTE cUiBtOn(UBYTE Action) { switch (Action) { case MENU_ON : { switch (VarsUi.State) { case 0 : // Turn BT on { VarsUi.BTCommand = (UBYTE)BTON; VarsUi.BTPar1 = (UBYTE)0; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.State++; } else { VarsUi.State = 99; } } break; case 1 : // Display turning on text { if (!cUiFeedback((BMPMAP*)&Wait,TXT_FB_BT_TURNING_ON_WAIT,0,0)) { VarsUi.State++; } } break; case 2 : // Check result { if (VarsUi.BTResult != INPROGRESS) { if (VarsUi.BTResult == SUCCESS) { Action = MENU_EXIT; } else { VarsUi.State++; } } } break; default : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_TURNING_ON_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; } } break; case MENU_OFF : { switch (VarsUi.State) { case 0 : // Turn BT off { VarsUi.BTCommand = (UBYTE)BTOFF; VarsUi.BTPar1 = (UBYTE)0; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.State++; } else { VarsUi.State = 99; } } break; case 1 : // Display turning off text { if (!cUiFeedback((BMPMAP*)&Wait,TXT_FB_BT_TURNING_OFF_WAIT,0,0)) { VarsUi.State++; } } break; case 2 : // Check result { if (VarsUi.BTResult != INPROGRESS) { if (VarsUi.BTResult == SUCCESS) { Action = MENU_EXIT; } else { VarsUi.State++; } } } break; default : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_TURNING_OFF_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; } } break; } if (Action == MENU_EXIT) { VarsUi.State = 0; IOMapUi.State = EXIT_PRESSED; } return (VarsUi.State); } //******* cUiBtVisiability *************************************************** UBYTE cUiBtVisiability(UBYTE Action) // Visibility on/off { switch (Action) { case MENU_ON : // Set visible { switch (VarsUi.State) { case 0 : { VarsUi.BTCommand = (UBYTE)VISIBILITY; VarsUi.BTPar1 = (UBYTE)1; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.State++; } else { Action = MENU_EXIT; } } break; default : { if (VarsUi.BTResult != INPROGRESS) { Action = MENU_EXIT; } } break; } } break; case MENU_OFF : // Set invisible { switch (VarsUi.State) { case 0 : { VarsUi.BTCommand = (UBYTE)VISIBILITY; VarsUi.BTPar1 = (UBYTE)0; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.State++; } else { Action = MENU_EXIT; } } break; default : { if (VarsUi.BTResult != INPROGRESS) { Action = MENU_EXIT; } } break; } } break; } if (Action == MENU_EXIT) { VarsUi.State = 0; IOMapUi.State = EXIT_PRESSED; } return (VarsUi.State); } //******* cUiBtSearch ******************************************************** UBYTE cUiBtSearch(UBYTE Action) // Search for devices { if (Action == MENU_INIT) // Init { switch (VarsUi.State) { case 0 : // Show three menu icons { pMapDisplay->pMenuIcons[MENUICON_LEFT] = pMapDisplay->pMenuIcons[MENUICON_CENTER]; pMapDisplay->pMenuIcons[MENUICON_RIGHT] = pMapDisplay->pMenuIcons[MENUICON_CENTER]; pMapDisplay->UpdateMask |= MENUICON_BITS; VarsUi.State++; } break; case 1 : // Display wait text and start search { if (!cUiFeedback((BMPMAP*)&Wait,TXT_FB_BT_SEARCHING_WAIT,0,0)) { VarsUi.BTCommand = (UBYTE)SEARCH; VarsUi.BTPar1 = (UBYTE)1; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.DisplayBuffer[0] = 0; pMapDisplay->pMenuText = VarsUi.DisplayBuffer; pMapDisplay->UpdateMask |= SPECIAL_BIT(MENUTEXT); VarsUi.NoOfNames = 0; VarsUi.NoOfDevices = 0; VarsUi.State++; } else { VarsUi.State = 99; } } } break; case 2 : // Wait for search finished { if (VarsUi.NoOfNames != pMapComm->BtDeviceNameCnt) { VarsUi.NoOfNames = pMapComm->BtDeviceNameCnt; if ((VarsUi.NoOfNames) && (VarsUi.NoOfNames <= DISPLAYLINE_LENGTH)) { sprintf((char*)VarsUi.DisplayBuffer,"%.*s",VarsUi.NoOfNames,"****************"); pMapDisplay->pMenuText = VarsUi.DisplayBuffer; pMapDisplay->UpdateMask |= SPECIAL_BIT(MENUTEXT); } } if (VarsUi.NoOfDevices != pMapComm->BtDeviceCnt) { VarsUi.NoOfDevices = pMapComm->BtDeviceCnt; if ((VarsUi.NoOfDevices) && (VarsUi.NoOfDevices <= DISPLAYLINE_LENGTH)) { sprintf((char*)VarsUi.DisplayBuffer,"%.*s",VarsUi.NoOfDevices,"????????????????"); pMapDisplay->pMenuText = VarsUi.DisplayBuffer; pMapDisplay->UpdateMask |= SPECIAL_BIT(MENUTEXT); } } if (VarsUi.BTResult != INPROGRESS) { cUiBTCommand(UI_BT_GET_DEVICES,0,&VarsUi.Devices,NULL); if (VarsUi.Devices) { VarsUi.State++; } else { VarsUi.State = 99; } } if (cUiReadButtons() == BUTTON_EXIT) { VarsUi.BTCommand = (UBYTE)STOPSEARCH; VarsUi.BTPar1 = (UBYTE)0; VarsUi.BTPar2 = (UBYTE)0; pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)); VarsUi.State = 4; } } break; case 3 : // Auto enter to next menu { IOMapUi.State = ENTER_PRESSED; VarsUi.State = 0; } break; case 4 : // Display info text { if (!cUiFeedback((BMPMAP*)&Info,TXT_FB_BT_SEARCH_ABORTED_INFO,0,DISPLAY_SHOW_TIME)) { VarsUi.State++; } } break; case 5 : // Wait for abort { if (VarsUi.BTResult != INPROGRESS) { cUiBTCommand(UI_BT_GET_DEVICES,0,&VarsUi.Devices,NULL); if (VarsUi.Devices) { VarsUi.State++; } else { VarsUi.State = 99; } } } break; case 6 : // Auto enter to next menu { IOMapUi.State = ENTER_PRESSED; VarsUi.State = 0; } break; default : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_SEARCHING_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { VarsUi.State = 0; IOMapUi.State = EXIT_PRESSED; } } break; } } return (VarsUi.State); } //******* cUiBtDeviceList **************************************************** UBYTE cUiBtDeviceList(UBYTE Action) // Show devices { switch (Action) { case MENU_INIT : // Init "Search" list { VarsUi.SelectedDevice = 0; VarsUi.DevicesKnown = 0; cUiBTCommand(UI_BT_GET_DEVICES,VarsUi.DevicesKnown,&VarsUi.Devices,NULL); if (VarsUi.Devices) { pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_BTDEVICELIST_SELECT); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); VarsUi.MenuIconTextSave = pMapDisplay->pMenuText; VarsUi.DeviceCenter = 1; Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } else { Action = MENU_EXIT; } } break; case MENU_INIT_ALTERNATIVE : // Init only "My contacts" { VarsUi.SelectedDevice = 0; VarsUi.DevicesKnown = 1; cUiBTCommand(UI_BT_GET_DEVICES,VarsUi.DevicesKnown,&VarsUi.Devices,NULL); if (VarsUi.Devices) { pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_BTDEVICELIST_SELECT); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); VarsUi.MenuIconTextSave = pMapDisplay->pMenuText; VarsUi.DeviceCenter = 1; Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } else { Action = MENU_EXIT; } } break; case MENU_LEFT : // Left button { cUiListLeft(VarsUi.Devices,&VarsUi.DeviceCenter); Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_RIGHT : // Right button { cUiListRight(VarsUi.Devices,&VarsUi.DeviceCenter); Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_SELECT : // Select for connection { VarsUi.SelectedDevice = VarsUi.DeviceCenter; pMapDisplay->pMenuText = VarsUi.MenuIconTextSave; IOMapUi.State = NEXT_MENU; } break; case MENU_DELETE : // Remove device from "My contacts" { switch (VarsUi.State) { case 0 : { if (VarsUi.SelectedDevice) { if (cUiBTGetDeviceIndex(VarsUi.DevicesKnown,VarsUi.SelectedDevice - 1,&VarsUi.BTIndex)) { VarsUi.BTCommand = (UBYTE)REMOVEDEVICE; VarsUi.BTPar1 = (UBYTE)VarsUi.BTIndex; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.State++; } else { VarsUi.State = 99; } } else { Action = MENU_EXIT; } VarsUi.SelectedDevice = 0; } else { Action = MENU_EXIT; } } break; case 1 : { if (VarsUi.BTResult != INPROGRESS) { if (VarsUi.BTResult == SUCCESS) { Action = MENU_EXIT; } else { VarsUi.State = 99; } } } break; default : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_REMOVE_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; } } break; } if (Action == MENU_DRAW) { cUiListCalc(VarsUi.Devices,&VarsUi.DeviceCenter,&VarsUi.DeviceLeft,&VarsUi.DeviceRight); pMapDisplay->pMenuIcons[MENUICON_LEFT] = NULL; pMapDisplay->pMenuIcons[MENUICON_CENTER] = NULL; pMapDisplay->pMenuIcons[MENUICON_RIGHT] = NULL; if (VarsUi.DeviceLeft) { VarsUi.Tmp = VarsUi.DeviceLeft - 1; cUiBTCommand(UI_BT_GET_DEVICE_TYPE,VarsUi.DevicesKnown,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_LEFT] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_LEFT); } if (VarsUi.DeviceCenter) { VarsUi.Tmp = VarsUi.DeviceCenter - 1; cUiBTCommand(UI_BT_GET_DEVICE_TYPE,VarsUi.DevicesKnown,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_CENTER); } if (VarsUi.DeviceRight) { VarsUi.Tmp = VarsUi.DeviceRight - 1; cUiBTCommand(UI_BT_GET_DEVICE_TYPE,VarsUi.DevicesKnown,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_RIGHT] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; pMapDisplay->UpdateMask |= MENUICON_BIT(MENUICON_RIGHT); } pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_5); VarsUi.Tmp = VarsUi.DeviceCenter - 1; cUiBTCommand(UI_BT_GET_DEVICE_NAME,VarsUi.DevicesKnown,&VarsUi.Tmp,VarsUi.DisplayBuffer); pMapDisplay->pMenuText = VarsUi.DisplayBuffer; pMapDisplay->EraseMask |= MENUICON_BITS; pMapDisplay->UpdateMask |= (SPECIAL_BIT(FRAME_SELECT) | SPECIAL_BIT(MENUTEXT)); } if (Action == MENU_EXIT) { VarsUi.State = 0; IOMapUi.State = EXIT_PRESSED; } return (VarsUi.State); } //******* cUiBtConnectList *************************************************** UBYTE cUiBtConnectList(UBYTE Action) // Show connections and maybe disconnect { switch (Action) { case MENU_INIT : // Init { VarsUi.Slots = SIZE_OF_BT_CONNECT_TABLE; VarsUi.SlotCenter = 2; Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_LEFT : // Left button { cUiListLeft(VarsUi.Slots,&VarsUi.SlotCenter); Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_RIGHT : // Right button { cUiListRight(VarsUi.Slots,&VarsUi.SlotCenter); Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_UPDATE : // Check connection valid { VarsUi.Tmp = VarsUi.SlotCenter - 1; if (cUiBTCommand(UI_BT_GET_CONNECTION_VALID,0,&VarsUi.Tmp,NULL) != UI_BT_SUCCES) { Action = MENU_EXIT; } } break; case MENU_DISCONNECT : // Disconnect { switch (VarsUi.State) { case 0 : { VarsUi.SelectedSlot = VarsUi.SlotCenter - 1; VarsUi.BTCommand = (UBYTE)DISCONNECT; VarsUi.BTPar1 = (UBYTE)VarsUi.SelectedSlot; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.State++; } else { VarsUi.State = 99; } } break; case 1 : { if (VarsUi.BTResult != INPROGRESS) { if (VarsUi.BTResult == SUCCESS) { Action = MENU_EXIT; } else { VarsUi.State = 99; } } } break; default : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_DISCONNECT_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; } } break; } if (Action == MENU_DRAW) { cUiListCalc(VarsUi.Slots,&VarsUi.SlotCenter,&VarsUi.SlotLeft,&VarsUi.SlotRight); pMapDisplay->pBitmaps[BITMAP_2] = (BMPMAP*)VarsUi.PortBitmapLeft; pMapDisplay->pBitmaps[BITMAP_3] = (BMPMAP*)VarsUi.PortBitmapCenter; pMapDisplay->pBitmaps[BITMAP_4] = (BMPMAP*)VarsUi.PortBitmapRight; VarsUi.Tmp = VarsUi.SlotLeft - 1; if (cUiBTCommand(UI_BT_GET_CONNECTION_VALID,0,&VarsUi.Tmp,NULL) == UI_BT_SUCCES) { cUiBTCommand(UI_BT_GET_CONNECTION_TYPE,0,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_LEFT] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; cUiDrawPortNo(VarsUi.PortBitmapLeft,MENUICON_LEFT,VarsUi.Tmp); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_2); } else { pMapDisplay->pMenuIcons[MENUICON_LEFT] = (UBYTE*)&Connections.Data[VarsUi.Tmp * Connections.ItemPixelsX * (Connections.ItemPixelsY / 8)]; } VarsUi.Tmp = VarsUi.SlotCenter - 1; cUiBTCommand(UI_BT_GET_CONNECTION_NAME,0,&VarsUi.Tmp,VarsUi.DisplayBuffer); pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_5); pMapDisplay->pMenuText = VarsUi.DisplayBuffer; if (cUiBTCommand(UI_BT_GET_CONNECTION_VALID,0,&VarsUi.Tmp,NULL) == UI_BT_SUCCES) { cUiBTCommand(UI_BT_GET_CONNECTION_TYPE,0,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; cUiDrawPortNo(VarsUi.PortBitmapCenter,MENUICON_CENTER,VarsUi.Tmp); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_3); } else { pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Connections.Data[VarsUi.Tmp * Connections.ItemPixelsX * (Connections.ItemPixelsY / 8)]; } VarsUi.Tmp = VarsUi.SlotRight - 1; if (cUiBTCommand(UI_BT_GET_CONNECTION_VALID,0,&VarsUi.Tmp,NULL) == UI_BT_SUCCES) { cUiBTCommand(UI_BT_GET_CONNECTION_TYPE,0,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_RIGHT] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; cUiDrawPortNo(VarsUi.PortBitmapRight,MENUICON_RIGHT,VarsUi.Tmp); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_4); } else { pMapDisplay->pMenuIcons[MENUICON_RIGHT] = (UBYTE*)&Connections.Data[VarsUi.Tmp * Connections.ItemPixelsX * (Connections.ItemPixelsY / 8)]; } pMapDisplay->EraseMask &= ~SCREEN_BIT(SCREEN_LARGE); pMapDisplay->EraseMask |= MENUICON_BITS; pMapDisplay->UpdateMask |= (MENUICON_BITS | SPECIAL_BIT(FRAME_SELECT) | SPECIAL_BIT(MENUTEXT)); } if (Action == MENU_EXIT) { VarsUi.State = 0; IOMapUi.State = EXIT_PRESSED; } return (VarsUi.State); } UBYTE cUiBtConnect(UBYTE Action) // Select connection no and insert device { switch (Action) { case MENU_INIT : // Init { VarsUi.Slots = SIZE_OF_BT_CONNECT_TABLE - 1; VarsUi.SlotCenter = 1; Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_LEFT : // Left button { cUiListLeft(VarsUi.Slots,&VarsUi.SlotCenter); Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_RIGHT : // Right button { cUiListRight(VarsUi.Slots,&VarsUi.SlotCenter); Action = MENU_DRAW; IOMapUi.State = TEST_BUTTONS; } break; case MENU_CONNECT : // Insert device { switch (VarsUi.State) { case 0 : // Check selected device { VarsUi.SelectedSlot = (UBYTE)VarsUi.SlotCenter; if (VarsUi.SelectedDevice) { VarsUi.State++; } else { Action = MENU_EXIT; } } break; case 1 : // Display wait text { if (!cUiFeedback((BMPMAP*)&Wait,TXT_FB_BT_CONNECTING_WAIT,0,0)) { if (cUiBTGetDeviceIndex(VarsUi.DevicesKnown,VarsUi.SelectedDevice - 1,&VarsUi.BTIndex)) { VarsUi.BTCommand = (UBYTE)CONNECT; VarsUi.BTPar1 = (UBYTE)VarsUi.BTIndex; VarsUi.BTPar2 = (UBYTE)VarsUi.SelectedSlot; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.State++; } else { VarsUi.State = 99; } } else { VarsUi.State = 99; } } } break; case 2 : // Wait for result { if (VarsUi.BTResult != INPROGRESS) { if (VarsUi.BTResult == SUCCESS) { Action = MENU_EXIT; } else { if (VarsUi.BTResult == REQPIN) { sprintf((char*)pMapSound->SoundFilename,"%s.%s",(char*)UI_ATTENTION_SOUND,(char*)TXT_FILE_EXT[FILETYPE_SOUND]); pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_ONCE; pMapSound->Flags |= SOUND_UPDATE; strcpy((char*)VarsUi.UserString,(char*)DEFAULT_PIN_CODE); VarsUi.State++; } else { VarsUi.State = 6; } } } } break; case 3 : // Get pincode and send { if (!cUiGetUserString(0)) { if (VarsUi.UserString[0] == 0) { sprintf((char*)VarsUi.UserString,"%08lX",VarsUi.CRPasskey); Action = MENU_EXIT; } else { VarsUi.State++; } pMapComm->pFunc2(VarsUi.UserString); } } break; case 4 : // Display wait text { if (!cUiFeedback((BMPMAP*)&Wait,TXT_FB_BT_CONNECTING_WAIT,0,0)) { VarsUi.State++; } } break; case 5 : // Wait for result { if (VarsUi.BTResult != INPROGRESS) { if (VarsUi.BTResult == SUCCESS) { Action = MENU_EXIT; } else { VarsUi.State = 6; } } } break; case 6 : // Display busy text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_CONNECT_BUSY_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; default : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_CONNECTING_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; } } break; case MENU_SEND : { switch (VarsUi.State) { case 0 : // Check connection { VarsUi.SelectedSlot = (UBYTE)VarsUi.SlotCenter; if (VarsUi.SelectedFilename[0] && (cUiBTCommand(UI_BT_GET_CONNECTION_NAME,0,&VarsUi.SelectedSlot,NULL) == UI_BT_SUCCES)) { VarsUi.State += 2; } else { VarsUi.State++; } } break; case 1 : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_SENDING_NO_CONN_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; case 2 : // Display wait text and send file { if (!cUiFeedback((BMPMAP*)&Wait,TXT_FB_BT_SENDING_WAIT,0,0)) { VarsUi.BTCommand = (UBYTE)SENDFILE; VarsUi.BTPar1 = (UBYTE)VarsUi.SelectedSlot; VarsUi.BTPar2 = (UBYTE)0; if (pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,VarsUi.SelectedFilename,&(VarsUi.BTResult)) == SUCCESS) { VarsUi.Timer = 0; VarsUi.State++; } else { VarsUi.State = 4; } } } break; case 3 : // Wait for result { if (VarsUi.BTResult != INPROGRESS) { if (VarsUi.BTResult == SUCCESS) { VarsUi.State += 2; } else { VarsUi.State++; } } VarsUi.Timer++; } break; case 4 : // Display fail text { if (!cUiFeedback((BMPMAP*)&Fail,TXT_FB_BT_SENDING_FAIL,0,DISPLAY_SHOW_ERROR_TIME)) { Action = MENU_EXIT; } } break; case 5 : // Wait min. "DISPLAY_SHOW_TIME" to show "TXT_FB_BT_SENDING_WAIT" { if (++VarsUi.Timer >= DISPLAY_SHOW_TIME) { Action = MENU_EXIT; } } break; } } break; } if (Action == MENU_DRAW) // Update display { cUiListCalc(VarsUi.Slots,&VarsUi.SlotCenter,&VarsUi.SlotLeft,&VarsUi.SlotRight); pMapDisplay->pBitmaps[BITMAP_2] = (BMPMAP*)VarsUi.PortBitmapLeft; pMapDisplay->pBitmaps[BITMAP_3] = (BMPMAP*)VarsUi.PortBitmapCenter; pMapDisplay->pBitmaps[BITMAP_4] = (BMPMAP*)VarsUi.PortBitmapRight; VarsUi.Tmp = VarsUi.SlotLeft; if (cUiBTCommand(UI_BT_GET_CONNECTION_VALID,0,&VarsUi.Tmp,NULL) == UI_BT_SUCCES) { cUiBTCommand(UI_BT_GET_CONNECTION_TYPE,0,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_LEFT] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; cUiDrawPortNo(VarsUi.PortBitmapLeft,MENUICON_LEFT,VarsUi.Tmp); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_2); } else { pMapDisplay->pMenuIcons[MENUICON_LEFT] = (UBYTE*)&Connections.Data[VarsUi.Tmp * Connections.ItemPixelsX * (Connections.ItemPixelsY / 8)]; } VarsUi.Tmp = VarsUi.SlotCenter; cUiBTCommand(UI_BT_GET_CONNECTION_NAME,0,&VarsUi.Tmp,VarsUi.DisplayBuffer); pMapDisplay->EraseMask |= TEXTLINE_BIT(TEXTLINE_5); pMapDisplay->pMenuText = VarsUi.DisplayBuffer; if (cUiBTCommand(UI_BT_GET_CONNECTION_VALID,0,&VarsUi.Tmp,NULL) == UI_BT_SUCCES) { cUiBTCommand(UI_BT_GET_CONNECTION_TYPE,0,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; cUiDrawPortNo(VarsUi.PortBitmapCenter,MENUICON_CENTER,VarsUi.Tmp); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_3); } else { pMapDisplay->pMenuIcons[MENUICON_CENTER] = (UBYTE*)&Connections.Data[VarsUi.Tmp * Connections.ItemPixelsX * (Connections.ItemPixelsY / 8)]; } VarsUi.Tmp = VarsUi.SlotRight; if (cUiBTCommand(UI_BT_GET_CONNECTION_VALID,0,&VarsUi.Tmp,NULL) == UI_BT_SUCCES) { cUiBTCommand(UI_BT_GET_CONNECTION_TYPE,0,&VarsUi.Tmp,&VarsUi.DeviceType); pMapDisplay->pMenuIcons[MENUICON_RIGHT] = (UBYTE*)&Devices.Data[VarsUi.DeviceType * Devices.ItemPixelsX * (Devices.ItemPixelsY / 8)]; cUiDrawPortNo(VarsUi.PortBitmapRight,MENUICON_RIGHT,VarsUi.Tmp); pMapDisplay->UpdateMask |= BITMAP_BIT(BITMAP_4); } else { pMapDisplay->pMenuIcons[MENUICON_RIGHT] = (UBYTE*)&Connections.Data[VarsUi.Tmp * Connections.ItemPixelsX * (Connections.ItemPixelsY / 8)]; } pMapDisplay->EraseMask &= ~SCREEN_BIT(SCREEN_LARGE); pMapDisplay->EraseMask |= MENUICON_BITS; pMapDisplay->UpdateMask |= (MENUICON_BITS | SPECIAL_BIT(FRAME_SELECT) | SPECIAL_BIT(MENUTEXT)); } if (Action == MENU_EXIT) { IOMapUi.State = EXIT_PRESSED; VarsUi.State = 0; } return (VarsUi.State); } //******* cUiPowerOffTime **************************************************** UBYTE cUiPowerOffTime(UBYTE Action) // MENU_INIT,MENU_LEFT,MENU_RIGHT,MENU_EXIT { switch (Action) { case MENU_INIT : // Init time counter and cursor bitmap { VarsUi.Counter = VarsUi.NVData.PowerdownCode + 1; VarsUi.pTmp = (UBYTE*)&Cursor; for (VarsUi.Tmp = 0;(VarsUi.Tmp < SIZE_OF_CURSOR) && (VarsUi.Tmp < Cursor_size);VarsUi.Tmp++) { VarsUi.CursorTmp[VarsUi.Tmp] = *VarsUi.pTmp; VarsUi.pTmp++; } Action = MENU_DRAW; } break; case MENU_LEFT : // Dec { cUiListLeft(POWER_OFF_TIME_STEPS,&VarsUi.Counter); Action = MENU_DRAW; } break; case MENU_RIGHT : // Inc { cUiListRight(POWER_OFF_TIME_STEPS,&VarsUi.Counter); Action = MENU_DRAW; } break; case MENU_ENTER : // Enter { VarsUi.NVData.PowerdownCode = VarsUi.Counter - 1; cUiNVWrite(); IOMapUi.SleepTimeout = PowerOffTimeSteps[VarsUi.NVData.PowerdownCode]; Action = MENU_EXIT; } break; } if (Action == MENU_DRAW) { if (VarsUi.Counter > 1) { sprintf((char*)VarsUi.DisplayBuffer,"%u",(UWORD)PowerOffTimeSteps[VarsUi.Counter - 1]); } else { sprintf((char*)VarsUi.DisplayBuffer,(char*)cUiGetString(TXT_POWEROFFTIME_NEVER)); } pMapDisplay->pTextLines[TEXTLINE_3] = VarsUi.DisplayBuffer; pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)VarsUi.CursorTmp; VarsUi.CursorTmp[4] = 46; VarsUi.CursorTmp[5] = 24; pMapDisplay->EraseMask |= (TEXTLINE_BIT(TEXTLINE_3) | TEXTLINE_BIT(TEXTLINE_4)); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= (TEXTLINE_BIT(TEXTLINE_3) | BITMAP_BIT(BITMAP_1)); } if (Action == MENU_EXIT) { IOMapUi.State = EXIT_PRESSED; } return (0); } //******* cUiBTConnectRequest ************************************************ UBYTE cUiBTConnectRequest(UBYTE Action) { switch (Action) { case MENU_INIT : { switch (VarsUi.CRState) { case 0 : { sprintf((char*)pMapSound->SoundFilename,"%s.%s",(char*)UI_ATTENTION_SOUND,(char*)TXT_FILE_EXT[FILETYPE_SOUND]); pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_ONCE; pMapSound->Flags |= SOUND_UPDATE; VarsUi.CRState++; } break; case 1 : { if (DISPLAY_IDLE) { pMapDisplay->Flags |= DISPLAY_POPUP; VarsUi.CRState++; } } break; case 2 : { strcpy((char*)VarsUi.UserString,(char*)DEFAULT_PIN_CODE); IOMapUi.Flags |= UI_REDRAW_STATUS; VarsUi.CRState++; } break; case 3 : // Get pincode and send { if (!cUiGetUserString(0)) { if (VarsUi.UserString[0] == 0) { sprintf((char*)VarsUi.UserString,"%08lX",VarsUi.CRPasskey); } pMapComm->pFunc2(VarsUi.UserString); VarsUi.CRState++; } } break; case 4 : { if (DISPLAY_IDLE) { pMapDisplay->Flags &= ~DISPLAY_POPUP; VarsUi.CRState = 0; } } break; } } break; } return (VarsUi.CRState); } //******* cUiFilesDelete ***************************************************** UBYTE cUiFilesDelete(UBYTE Action) { switch (Action) { case MENU_INIT : { pMapDisplay->pTextLines[TEXTLINE_3] = cUiGetString(TXT_FILESDELETE_DELETING_ALL); pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_3); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_3); sprintf((char*)VarsUi.DisplayBuffer,(char*)cUiGetString(TXT_FILESDELETE_S_FILES),(char*)cUiGetString(TXT_FILETYPE[VarsUi.SelectedType])); pMapDisplay->pTextLines[TEXTLINE_4] = VarsUi.DisplayBuffer; pMapDisplay->TextLinesCenterFlags |= TEXTLINE_BIT(TEXTLINE_4); pMapDisplay->UpdateMask |= TEXTLINE_BIT(TEXTLINE_4); IOMapUi.State = TEST_BUTTONS; } break; case MENU_DELETE : { switch (VarsUi.State) { case 0 : { if (VarsUi.SelectedType < FILETYPES) { sprintf((char*)VarsUi.FilenameBuffer,"*.%s",TXT_FILE_EXT[VarsUi.SelectedType]); } else { sprintf((char*)VarsUi.FilenameBuffer,"*.*"); } VarsUi.State++; } break; case 1 : { if (SOUND_IDLE == pMapSound->State) { VarsUi.State++; } } break; case 2 : // Delete files { VarsUi.TmpHandle = pMapLoader->pFunc(FINDFIRST,VarsUi.FilenameBuffer,VarsUi.SelectedFilename,&VarsUi.TmpLength); if (!(VarsUi.TmpHandle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsUi.TmpHandle,NULL,NULL); pMapLoader->pFunc(DELETE,VarsUi.SelectedFilename,NULL,NULL); } else { pMapDisplay->EraseMask |= MENUICON_BITS; pMapDisplay->EraseMask |= SPECIAL_BIT(MENUTEXT); VarsUi.State++; } } break; default : // Display Files deleted text { if (!cUiFeedback((BMPMAP*)&Info,TXT_FB_FD_FILES_INFO,TXT_FB_FD_DELETED_INFO,DISPLAY_SHOW_TIME)) { IOMapUi.State = EXIT_PRESSED; VarsUi.State = 0; } } break; } } break; default : { if (Action < FILETYPES) { VarsUi.SelectedType = Action; } else { VarsUi.SelectedType = FILETYPE_ALL; } } break; } return (VarsUi.State); } //******* cUiOff ************************************************************* UBYTE cUiOff(UBYTE Action) // Tell AVR to turn off ARM { if (Action == MENU_INIT) { switch (VarsUi.State) { case 0 : // Stop VM if running { if (pMapCmd->ProgStatus == PROG_RUNNING) { pMapCmd->DeactivateFlag = TRUE; } VarsUi.State++; } break; case 1 : // When VM is stopped -> Display off and close all connections { if (pMapCmd->ProgStatus != PROG_RUNNING) { pMapDisplay->Flags &= ~DISPLAY_ON; VarsUi.BTCommand = (UBYTE)DISCONNECTALL; VarsUi.BTPar1 = (UBYTE)0; VarsUi.BTPar2 = (UBYTE)0; pMapComm->pFunc(VarsUi.BTCommand,VarsUi.BTPar1,VarsUi.BTPar2,0,NULL,&(VarsUi.BTResult)); VarsUi.State++; } } break; case 2 : // Send off command to AVR { if (VarsUi.BTResult != INPROGRESS) { pMapIoCtrl->PowerOn = POWERDOWN; VarsUi.Timer = 0; VarsUi.State++; } } break; case 3 : // Wait for power off { if (++VarsUi.Timer >= ARM_WAIT_FOR_POWER_OFF) { VarsUi.State++; } } break; case 4 : // Vitual off state (if still power) wait for on button { pMapIoCtrl->PowerOn = 0; if (BUTTON_ENTER == cUiReadButtons()) { VarsUi.State++; } } break; default : // Turn on again { IOMapUi.State = INIT_DISPLAY; VarsUi.State = 0; } break; } } return (VarsUi.State); } //******* FUNCTIONS ********************************************************** enum FUNC_NO // Must reffer to entry in Functions { // used in Menus to repressent function FUNC_NO_NOT_USED = 0x00, FUNC_NO_TEST_PROGRAM = 0x01, FUNC_NO_OFF = 0x02, FUNC_NO_BT_ON = 0x03, FUNC_NO_POWER_OFF_TIME = 0x04, FUNC_NO_FILES_DELETE = 0x05, FUNC_NO_FILE_LIST = 0x06, FUNC_NO_VOLUME = 0x07, FUNC_NO_FILE_RUN = 0x08, FUNC_NO_FILE_DELETE = 0x09, FUNC_NO_FREE1 = 0x0A, FUNC_NO_ON_BRICK_PROGRAMMING = 0x0B, FUNC_NO_FREE2 = 0x0C, FUNC_NO_BT_CONNECT_REQUEST = 0x0D, FUNC_NO_VIEW = 0x0E, FUNC_NO_GET_USER_STRING = 0x0F, FUNC_NO_BT_CONNECT = 0x10, FUNC_NO_BT_VISIABILITY = 0x11, FUNC_NO_BT_SEARCH = 0x12, FUNC_NO_BT_DEVICE_LIST = 0x13, FUNC_NO_BT_CONNECT_LIST = 0x14, FUNC_NO_MAX }; FUNCTION Functions[] = // Use same index as FUNC_NO { 0, TestPrg, cUiOff, cUiBtOn, cUiPowerOffTime, cUiFilesDelete, cUiFileList, cUiVolume, cUiFileRun, cUiFileDelete, cUiDataLogging, cUiOnBrickProgramming, 0, cUiBTConnectRequest, cUiView, cUiGetUserString, cUiBtConnect, cUiBtVisiability, cUiBtSearch, cUiBtDeviceList, cUiBtConnectList }; nxt-firmware-1.29.7/src/Ui_txt.h000066400000000000000000000021411466344546000164630ustar00rootroot00000000000000/* Created from previous Ui.txt. Covered by the LEGO Open Source License. */ const char * const Ui[] = { "Connecting", "Line is busy", "Failed!", "Connection?", "Sending file", "Failed!", "Turning on", "Failed!", "Turning off", "Failed!", "Searching", "Aborted!", "Failed!", "Failed!", "Failed!", "Memory full!", "File saved", "File exists", "overwrite!", "Saved as", "File exist", "overwrite!", "File deleted", "Files", "deleted", "Running", "Aborted!", "Done", "File error!", "Deleting all", "%s files!", "Press Clear to", "stop DataLogging", "Port occupied!", "H:MM:SS:00", "HH:MM:SS", "Sound", "Software", "NXT", "Try Me", "Datalog", "Passkey:", "File name:", "Please use port:", "1 - Touch Sensor", "2 - Sound Sensor", "3 - Light Sensor", "4 - Ultrasonic ", "B/C - L/R motors", "Select", "Select", "Select", "BT save data", "error!", "BT store is", "full error!", "BT unknown", "addr. error!", "Memory is", "full!", "Never", }; nxt-firmware-1.29.7/src/c_button.c000066400000000000000000000063221466344546000170240ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_button.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_butt $ // // Platform C // #include "stdconst.h" #include "modules.h" #include "c_button.h" #include "c_button.iom" #include "c_button.h" #include "d_button.h" #define BTN_PRESCALER 2 enum { LONG_TIME = (2000/BTN_PRESCALER) }; static IOMAPBUTTON IOMapButton; static VARSBUTTON VarsButton; static UBYTE BtnCnt; const HEADER cButton = { 0x00040001L, "Button", cButtonInit, cButtonCtrl, cButtonExit, (void *)&IOMapButton, (void *)&VarsButton, (UWORD)sizeof(IOMapButton), (UWORD)sizeof(VarsButton), 0x0000 //Code size - not used so far }; void cButtonInit(void* pHeader) { UBYTE Tmp; for (Tmp = 0; Tmp < NO_OF_BTNS; Tmp++) { IOMapButton.State[Tmp] = 0; IOMapButton.BtnCnt[Tmp].PressedCnt = 0; IOMapButton.BtnCnt[Tmp].LongPressCnt = 0; IOMapButton.BtnCnt[Tmp].ShortRelCnt = 0; IOMapButton.BtnCnt[Tmp].LongRelCnt = 0; VarsButton.Cnt[Tmp] = 0; } VarsButton.OldState = 0; BtnCnt = 0; dButtonInit(BTN_PRESCALER); } void cButtonCtrl(void) { UBYTE ButtonState, Tmp, ButtonNo; for (Tmp = 0; Tmp < NO_OF_BTNS; Tmp++) { IOMapButton.State[Tmp] &= ~PRESSED_EV; } if (++BtnCnt >= BTN_PRESCALER) { BtnCnt = 0; dButtonRead(&ButtonState); ButtonNo = 0x01; for (Tmp = 0; Tmp < NO_OF_BTNS; Tmp++) { if (ButtonState & ButtonNo) { if (LONG_TIME >= (VarsButton.Cnt[Tmp])) { (VarsButton.Cnt[Tmp])++; } IOMapButton.State[Tmp] = PRESSED_STATE; if (!((VarsButton.OldState) & ButtonNo)) { /* Button just pressed */ (IOMapButton.State[Tmp]) |= PRESSED_EV; (IOMapButton.BtnCnt[Tmp].PressedCnt)++; VarsButton.Cnt[Tmp] = 0; } else { if (LONG_TIME == VarsButton.Cnt[Tmp]) { IOMapButton.State[Tmp] |= LONG_PRESSED_EV; (IOMapButton.BtnCnt[Tmp].LongPressCnt)++; } } } else { IOMapButton.State[Tmp] = 0x00; if ((VarsButton.OldState) & ButtonNo) { if (VarsButton.Cnt[Tmp] > LONG_TIME) { IOMapButton.State[Tmp] = LONG_RELEASED_EV; (IOMapButton.BtnCnt[Tmp].LongRelCnt)++; } else { IOMapButton.State[Tmp] = SHORT_RELEASED_EV; (IOMapButton.BtnCnt[Tmp].ShortRelCnt)++; } } } ButtonNo <<= 1; IOMapButton.BtnCnt[Tmp].RelCnt = ((IOMapButton.BtnCnt[Tmp].ShortRelCnt) + (IOMapButton.BtnCnt[Tmp].LongRelCnt)); } VarsButton.OldState = ButtonState; } } void cButtonExit(void) { dButtonExit(); } nxt-firmware-1.29.7/src/c_button.h000066400000000000000000000014021466344546000170230ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_button.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_butt $ // // Platform C // #ifndef C_BUTTON #define C_BUTTON #ifdef INCLUDE_OS extern const HEADER cButton; #endif #include "c_button.iom" typedef struct { UWORD Cnt[NO_OF_BTNS]; UBYTE OldState; }VARSBUTTON; void cButtonInit(void* pHeader); void cButtonCtrl(void); void cButtonExit(void); extern const HEADER cButton; #endif nxt-firmware-1.29.7/src/c_button.iom000066400000000000000000000021451466344546000173650ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_button.iom $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_butt $ // // Platform C // #ifndef CBUTTON_IOM #define CBUTTON_IOM #define pMapButton ((IOMAPBUTTON*)(pHeaders[ENTRY_BUTTON]->pIOMap)) enum { BTN1, BTN2, BTN3, BTN4, NO_OF_BTNS }; /* Costants related to State */ enum { PRESSED_EV = 0x01, SHORT_RELEASED_EV = 0x02, LONG_PRESSED_EV = 0x04, LONG_RELEASED_EV = 0x08, PRESSED_STATE = 0x80 }; typedef struct { UBYTE PressedCnt; UBYTE LongPressCnt; UBYTE ShortRelCnt; UBYTE LongRelCnt; UBYTE RelCnt; UBYTE SpareOne; UBYTE SpareTwo; UBYTE SpareThree; }BTNCNT; typedef struct { BTNCNT BtnCnt[NO_OF_BTNS]; UBYTE State[NO_OF_BTNS]; }IOMAPBUTTON; #endif nxt-firmware-1.29.7/src/c_cmd.c000066400000000000000000007064431466344546000162670ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date: 24-06-09 8:53 $ // // Filename $Workfile:: c_cmd.c $ // // Version $Revision: 14 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_cmd. $ // // Platform C // // // File Description: // This file contains the virtual machine implementation to run bytecode // programs compatible with LEGO MINDSTORMS NXT Software 2.0. // // This module (c_cmd) is also responsible for reading the system timer // (d_timer) and returning on 1 ms timer boundaries. // #include "stdconst.h" #include "modules.h" #include "c_cmd.iom" #include "c_output.iom" #include "c_input.iom" #include "c_loader.iom" #include "c_ui.iom" #include "c_sound.iom" #include "c_button.iom" #include "c_display.iom" #include "c_comm.iom" #include "c_lowspeed.iom" #include "m_sched.h" #include "c_cmd.h" #include "c_cmd_bytecodes.h" #include "d_timer.h" #include #include #include #include // for sqrt, abs, and trig stuff #define VMProfilingCode 0 static IOMAPCMD IOMapCmd; static VARSCMD VarsCmd; static HEADER **pHeaders; static ULONG gInstrsToExecute; static SLONG gPCDelta; #define NUM_INTERP_FUNCS 16 #define NUM_SHORT_INTERP_FUNCS 8 #define VAR_INSTR_SIZE 0xE // important to cast since most args are assigned from signed value, and locals may be ULONG #define GetDataArg(arg) ((UWORD)(arg)) #if VMProfilingCode static ULONG ExecutedInstrs= 0, CmdCtrlTime= 0, OverheadTime= 0, CmdCtrlCalls= 0, LeaveTime= 0, NotFirstCall= 0, LastAvgCount= 0; static ULONG CmdCtrlClumpTime[256]; typedef struct { ULONG Time; ULONG Count; ULONG Avg; ULONG Max; } VMInstrProfileInfo; static VMInstrProfileInfo InstrProfile[OPCODE_COUNT]; static VMInstrProfileInfo SysCallProfile[SYSCALL_COUNT]; static VMInstrProfileInfo InterpFuncProfile[NUM_INTERP_FUNCS]; static VMInstrProfileInfo ShortInstrProfile[NUM_SHORT_OPCODE_COUNT]; #endif #define cCmdDSType(Arg) (VarsCmd.pDataspaceTOC[(Arg)].TypeCode) #define cCmdDSScalarPtr(DSElementID, Offset) (VarsCmd.pDataspace + VarsCmd.pDataspaceTOC[DSElementID].DSOffset + Offset) #define cCmdSizeOf(TC) (TC_Size_Table[(TC)]) #define scalarBinopDispatchMask 0x1 #define scalarUnop2DispatchMask 0x2 const HEADER cCmd = { 0x00010001L, "Command", cCmdInit, cCmdCtrl, cCmdExit, (void *)&IOMapCmd, (void *)&VarsCmd, (UWORD)sizeof(IOMapCmd), (UWORD)sizeof(VarsCmd), 0x0000 //Code size - not used so far }; #if ENABLE_VM // c_cmd_drawing.inc is just another C source file // (the graphics implementation was split off for practical file management reasons) #include "c_cmd_drawing.inc" // //Function pointers to sub-interpreters //This table is indexed by instr size //Unary operations can have arity of 1 or 2 (some need a destination) //All instructions taking 4 or more operands are handled as "Other" // Table uses NoArg for illegal instr sizes such as zero and odd sizes // static pInterp InterpFuncs[NUM_INTERP_FUNCS] = { cCmdInterpNoArg, cCmdInterpNoArg, cCmdInterpNoArg, // size 2 cCmdInterpNoArg, cCmdInterpUnop1, // size 4 cCmdInterpNoArg, cCmdInterpUnop2, // size 6 general poly is cCmdInterpUnop2, scalar is cCmdInterpScalarUnop2 cCmdInterpNoArg, cCmdInterpBinop, // size 8, general poly is cCmdInterpBinop, scalar is cCmdInterpScalarBinop cCmdInterpNoArg, cCmdInterpOther, // size 10 cCmdInterpNoArg, cCmdInterpOther, // size 12 cCmdInterpNoArg, cCmdInterpOther, // size 14 cCmdInterpNoArg }; static pInterpShort ShortInterpFuncs[NUM_SHORT_INTERP_FUNCS] = { cCmdInterpShortMove, cCmdInterpShortAcquire, cCmdInterpShortRelease, cCmdInterpShortSubCall, cCmdInterpShortError, cCmdInterpShortError, cCmdInterpShortError, cCmdInterpShortError }; ULONG TC_Size_Table[]= { 0, // void SIZE_UBYTE, SIZE_SBYTE, SIZE_UWORD, SIZE_SWORD, SIZE_ULONG, SIZE_SLONG, SIZE_UWORD, // array 0, // cluster SIZE_MUTEX, SIZE_FLOAT }; // //Function pointers to SysCall implementations //See interpreter for OP_SYSCALL // static pSysCall SysCallFuncs[SYSCALL_COUNT] = { cCmdWrapFileOpenRead, cCmdWrapFileOpenWrite, cCmdWrapFileOpenAppend, cCmdWrapFileRead, cCmdWrapFileWrite, cCmdWrapFileClose, // 5 cCmdWrapFileResolveHandle, cCmdWrapFileRename, cCmdWrapFileDelete, cCmdWrapSoundPlayFile, cCmdWrapSoundPlayTone, // 10 cCmdWrapSoundGetState, cCmdWrapSoundSetState, cCmdWrapDrawText, cCmdWrapDrawPoint, cCmdWrapDrawLine, // 15 cCmdWrapDrawCircle, cCmdWrapDrawRect, cCmdWrapDrawPicture, cCmdWrapSetScreenMode, cCmdWrapReadButton, // 20 cCmdWrapCommLSWrite, cCmdWrapCommLSRead, cCmdWrapCommLSCheckStatus, cCmdWrapRandomNumber, cCmdWrapGetStartTick, // 25 cCmdWrapMessageWrite, cCmdWrapMessageRead, cCmdWrapCommBTCheckStatus, cCmdWrapCommBTWrite, cCmdWrapCommBTRead, // 30 cCmdWrapKeepAlive, cCmdWrapIOMapRead, cCmdWrapIOMapWrite, cCmdWrapColorSensorRead, cCmdWrapCommBTOnOff, // 35 cCmdWrapCommBTConnection, cCmdWrapCommHSWrite, cCmdWrapCommHSRead, cCmdWrapCommHSCheckStatus, cCmdWrapReadSemData, //40 cCmdWrapWriteSemData, cCmdWrapComputeCalibValue, cCmdWrapUpdateCalibCacheInfo, cCmdWrapDatalogWrite, cCmdWrapDatalogGetTimes, //45 cCmdWrapSetSleepTimeout, cCmdWrapListFiles //47 // don't forget to update SYSCALL_COUNT in c_cmd.h }; // //Next set of arrays are lookup tables for IOM access bytecodes // TYPE_CODE IO_TYPES_IN[IO_IN_FIELD_COUNT] = { //IO_IN0 TC_UBYTE, //IO_IN_TYPE TC_UBYTE, //IO_IN_MODE TC_UWORD, //IO_IN_ADRAW TC_UWORD, //IO_IN_NORMRAW TC_SWORD, //IO_IN_SCALED_VAL TC_UBYTE, //IO_IN_INVALID_DATA //IO_IN1 TC_UBYTE, //IO_IN_TYPE TC_UBYTE, //IO_IN_MODE TC_UWORD, //IO_IN_ADRAW TC_UWORD, //IO_IN_NORMRAW TC_SWORD, //IO_IN_SCALED_VAL TC_UBYTE, //IO_IN_INVALID_DATA //IO_IN2 TC_UBYTE, //IO_IN_TYPE TC_UBYTE, //IO_IN_MODE TC_UWORD, //IO_IN_ADRAW TC_UWORD, //IO_IN_NORMRAW TC_SWORD, //IO_IN_SCALED_VAL TC_UBYTE, //IO_IN_INVALID_DATA //IO_IN3 TC_UBYTE, //IO_IN_TYPE TC_UBYTE, //IO_IN_MODE TC_UWORD, //IO_IN_ADRAW TC_UWORD, //IO_IN_NORMRAW TC_SWORD, //IO_IN_SCALED_VAL TC_UBYTE, //IO_IN_INVALID_DATA }; TYPE_CODE IO_TYPES_OUT[IO_OUT_FIELD_COUNT] = { //IO_OUT0 TC_UBYTE, //IO_OUT_FLAGS TC_UBYTE, //IO_OUT_MODE TC_SBYTE, //IO_OUT_SPEED TC_SBYTE, //IO_OUT_ACTUAL_SPEED TC_SLONG, //IO_OUT_TACH_COUNT TC_ULONG, //IO_OUT_TACH_LIMIT TC_UBYTE, //IO_OUT_RUN_STATE TC_SBYTE, //IO_OUT_TURN_RATIO TC_UBYTE, //IO_OUT_REG_MODE TC_UBYTE, //IO_OUT_OVERLOAD TC_UBYTE, //IO_OUT_REG_P_VAL TC_UBYTE, //IO_OUT_REG_I_VAL TC_UBYTE, //IO_OUT_REG_D_VAL TC_SLONG, //IO_OUT_BLOCK_TACH_COUNT TC_SLONG, //IO_OUT_ROTATION_COUNT TC_UBYTE, //IO_OUT_OPTIONS TC_SBYTE, //IO_OUT_MAX_SPEED TC_SBYTE, //IO_OUT_MAX_ACCELERATION //IO_OUT1 TC_UBYTE, //IO_OUT_FLAGS TC_UBYTE, //IO_OUT_MODE TC_SBYTE, //IO_OUT_SPEED TC_SBYTE, //IO_OUT_ACTUAL_SPEED TC_SLONG, //IO_OUT_TACH_COUNT TC_ULONG, //IO_OUT_TACH_LIMIT TC_UBYTE, //IO_OUT_RUN_STATE TC_SBYTE, //IO_OUT_TURN_RATIO TC_UBYTE, //IO_OUT_REG_MODE TC_UBYTE, //IO_OUT_OVERLOAD TC_UBYTE, //IO_OUT_REG_P_VAL TC_UBYTE, //IO_OUT_REG_I_VAL TC_UBYTE, //IO_OUT_REG_D_VAL TC_SLONG, //IO_OUT_BLOCK_TACH_COUNT TC_SLONG, //IO_OUT_ROTATION_COUNT TC_UBYTE, //IO_OUT_OPTIONS TC_SBYTE, //IO_OUT_MAX_SPEED TC_SBYTE, //IO_OUT_MAX_ACCELERATION //IO_OUT2 TC_UBYTE, //IO_OUT_FLAGS TC_UBYTE, //IO_OUT_MODE TC_SBYTE, //IO_OUT_SPEED TC_SBYTE, //IO_OUT_ACTUAL_SPEED TC_SLONG, //IO_OUT_TACH_COUNT TC_ULONG, //IO_OUT_TACH_LIMIT TC_UBYTE, //IO_OUT_RUN_STATE TC_SBYTE, //IO_OUT_TURN_RATIO TC_UBYTE, //IO_OUT_REG_MODE TC_UBYTE, //IO_OUT_OVERLOAD TC_UBYTE, //IO_OUT_REG_P_VAL TC_UBYTE, //IO_OUT_REG_I_VAL TC_UBYTE, //IO_OUT_REG_D_VAL TC_SLONG, //IO_OUT_BLOCK_TACH_COUNT TC_SLONG, //IO_OUT_ROTATION_COUNT TC_UBYTE, //IO_OUT_OPTIONS TC_SBYTE, //IO_OUT_MAX_SPEED TC_SBYTE, //IO_OUT_MAX_ACCELERATION }; TYPE_CODE * IO_TYPES[2] = { IO_TYPES_IN, IO_TYPES_OUT }; //Actual pointers filled in during cCmdInit() void * IO_PTRS_IN[IO_IN_FIELD_COUNT]; void * IO_PTRS_OUT[IO_OUT_FIELD_COUNT]; void ** IO_PTRS[2] = { IO_PTRS_IN, IO_PTRS_OUT }; // Data used to indicate usage of motor ports, or usage requests UBYTE gUsageSemData, gRequestSemData; UBYTE cCmdBTGetDeviceType(UBYTE *pCOD) { ULONG COD; UBYTE Result; UBYTE Tmp; COD = 0; for (Tmp = 0;Tmp < SIZE_OF_CLASS_OF_DEVICE;Tmp++) { COD <<= 8; COD |= (ULONG)*pCOD; pCOD++; } Result = DEVICETYPE_UNKNOWN; if ((COD & 0x00001FFF) == 0x00000804) { Result = DEVICETYPE_NXT; } if ((COD & 0x00001F00) == 0x00000200) { Result = DEVICETYPE_PHONE; } if ((COD & 0x00001F00) == 0x00000100) { Result = DEVICETYPE_PC; } return (Result); } //cCmdHandleRemoteCommands is the registered handler for "direct" command protocol packets //It is only intended to be called via c_comm's main protocol handler UWORD cCmdHandleRemoteCommands(UBYTE * pInBuf, UBYTE * pOutBuf, UBYTE * pLen) { NXT_STATUS RCStatus = NO_ERR; //Response packet length. Always includes RCStatus byte. ULONG ResponseLen = 1; //Boolean flag to send a response. TRUE unless overridden below. ULONG SendResponse = TRUE; //Boolean flag if we are handling a reply telegram. FALSE unless overridden. ULONG IncomingReply = FALSE; ULONG i, FirstPort, LastPort; UWORD LStatus; UWORD Count, QueueID; UBYTE * pData; //Illegal call, give up if (pInBuf == NULL || pLen == NULL) { NXT_BREAK; return (0xFFFF); } //No output buffer provided, so skip any work related to returning a response if (pOutBuf == NULL) SendResponse = FALSE; //If first byte identifies this as a reply telegram, we have different work to do. if (pInBuf[0] == 0x02) { IncomingReply = TRUE; //Reply telegrams never get responses, even if caller provided a buffer. SendResponse = FALSE; } //Advance pInBuf past command type byte pInBuf++; if (!IncomingReply) { switch(pInBuf[0]) { case RC_START_PROGRAM: { //Check that file exists. If not, return error //!!! Should return standard loader file error in cases like this?? //!!! Proper solution would also check file mode to avoid confusing errors if (LOADER_ERR(LStatus = pMapLoader->pFunc(FINDFIRST, (&pInBuf[1]), NULL, NULL)) != SUCCESS) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } //Close file handle returned by FINDFIRST pMapLoader->pFunc(CLOSE, LOADER_HANDLE_P(LStatus), NULL, NULL); //File must exist, so inform UI to attempt execution in the usual way (enables consistent feedback) pMapUi->Flags |= UI_EXECUTE_LMS_FILE; strncpy((PSZ)(pMapUi->LMSfilename), (PSZ)(&pInBuf[1]), FILENAME_LENGTH + 1); } break; case RC_STOP_PROGRAM: { if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) { RCStatus = ERR_NO_PROG; break; } IOMapCmd.DeactivateFlag = TRUE; } break; case RC_PLAY_SOUND_FILE: { if (LOADER_ERR(LStatus = pMapLoader->pFunc(FINDFIRST, (&pInBuf[2]), NULL, NULL)) != SUCCESS) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } //Close file handle returned by FINDFIRST pMapLoader->pFunc(CLOSE, LOADER_HANDLE_P(LStatus), NULL, NULL); if (pInBuf[1] == FALSE) pMapSound->Mode = SOUND_ONCE; else //Any non-zero value treated as TRUE pMapSound->Mode = SOUND_LOOP; strncpy((PSZ)pMapSound->SoundFilename, (PSZ)(&pInBuf[2]), FILENAME_LENGTH + 1); pMapSound->Flags |= SOUND_UPDATE; } break; case RC_PLAY_TONE: { pMapSound->Mode = SOUND_TONE; //!!! Range check valid values? memcpy((PSZ)(&(pMapSound->Freq)), (PSZ)(&pInBuf[1]), 2); memcpy((PSZ)(&(pMapSound->Duration)), (PSZ)(&pInBuf[3]), 2); pMapSound->Flags |= SOUND_UPDATE; } break; case RC_SET_OUT_STATE: { UBYTE Port = pInBuf[1]; //Don't do anything if illegal port specification is made if (Port >= NO_OF_OUTPUTS && Port != 0xFF) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } //0xFF is protocol defined to mean "all ports". if (Port == 0xFF) { FirstPort = 0; LastPort = NO_OF_OUTPUTS - 1; } else FirstPort = LastPort = Port; for (i = FirstPort; i <= LastPort; i++) { OUTPUT * pOut = &(pMapOutPut->Outputs[i]); pOut->Speed = pInBuf[2]; pOut->Mode = pInBuf[3]; pOut->RegMode = pInBuf[4]; pOut->SyncTurnParameter = pInBuf[5]; pOut->RunState = pInBuf[6]; pOut->Options = pOut->Mode & REG_METHOD; memcpy((PSZ)(&(pOut->TachoLimit)), (PSZ)(&pInBuf[7]), 4); pOut->Flags |= UPDATE_MODE | UPDATE_SPEED | UPDATE_TACHO_LIMIT; } } break; case RC_SET_IN_MODE: { i = pInBuf[1]; //Don't do anything if illegal port specification is made //!!! Should check against legal Types and Modes? (bitmask for Modes?) if (i >= NO_OF_INPUTS) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } INPUTSTRUCT * pIn = &(pMapInput->Inputs[i]); pIn->SensorType = pInBuf[2]; pIn->SensorMode = pInBuf[3]; //Set InvalidData flag automatically since type may have changed pIn->InvalidData = TRUE; } break; case RC_GET_OUT_STATE: { if (SendResponse == TRUE) { i = pInBuf[1]; //Return error and all zeros if illegal port specification is made if (i >= NO_OF_OUTPUTS) { RCStatus = ERR_RC_ILLEGAL_VAL; memset(&(pOutBuf[ResponseLen]), 0, 22); ResponseLen += 22; break; } OUTPUT * pOut = &(pMapOutPut->Outputs[i]); //Echo port pOutBuf[ResponseLen] = i; ResponseLen++; //Power pOutBuf[ResponseLen] = pOut->Speed; ResponseLen++; //Mode pOutBuf[ResponseLen] = pOut->Mode; ResponseLen++; //RegMode pOutBuf[ResponseLen] = pOut->RegMode; ResponseLen++; //TurnRatio pOutBuf[ResponseLen] = pOut->SyncTurnParameter; ResponseLen++; //RunState pOutBuf[ResponseLen] = pOut->RunState; ResponseLen++; //TachoLimit ULONG memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pOut->TachoLimit)), 4); ResponseLen += 4; //TachoCount SLONG memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pOut->TachoCnt)), 4); ResponseLen += 4; //BlockTachoCount SLONG memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pOut->BlockTachoCount)), 4); ResponseLen += 4; //RotationCount SLONG memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pOut->RotationCount)), 4); ResponseLen += 4; NXT_ASSERT(ResponseLen == 23); } } break; case RC_GET_IN_VALS: { if (SendResponse == TRUE) { i = pInBuf[1]; //Return error and all zeros if illegal port specification is made if (i >= NO_OF_INPUTS) { RCStatus = ERR_RC_ILLEGAL_VAL; memset(&(pOutBuf[ResponseLen]), 0, 13); ResponseLen += 13; break; } //Echo port pOutBuf[ResponseLen] = i; ResponseLen++; INPUTSTRUCT * pIn = &(pMapInput->Inputs[i]); //Set "Valid?" boolean if (pIn->InvalidData) pOutBuf[ResponseLen] = FALSE; else pOutBuf[ResponseLen] = TRUE; ResponseLen++; //Set "Calibrated?" boolean //!!! "Calibrated?" is a placeholder in the protocol. Always FALSE for now. pOutBuf[ResponseLen] = FALSE; ResponseLen++; pOutBuf[ResponseLen] = pIn->SensorType; ResponseLen++; pOutBuf[ResponseLen] = pIn->SensorMode; ResponseLen++; //Set Raw, Normalized, and Scaled values memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pIn->ADRaw)), 2); ResponseLen += 2; memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pIn->SensorRaw)), 2); ResponseLen += 2; memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pIn->SensorValue)), 2); ResponseLen += 2; //!!! Return normalized raw value in place of calibrated value for now -- see comment above memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)(&(pIn->SensorRaw)), 2); ResponseLen += 2; NXT_ASSERT(ResponseLen == 14); } } break; case RC_RESET_IN_VAL: { i = pInBuf[1]; //Don't do anything if illegal port specification is made if (i >= NO_OF_INPUTS) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } //Clear SensorValue to zero. Leave Raw and Normalized as-is, since they never accumulate running values. pMapInput->Inputs[i].SensorValue = 0; } break; case RC_MESSAGE_WRITE: { QueueID = pInBuf[1]; Count = pInBuf[2]; pData = &(pInBuf[3]); //If Count is illegal or MsgData is not null-terminated, // we can't accept it as a valid string if (Count == 0 || Count > MAX_MESSAGE_SIZE || pData[Count - 1] != 0x00) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } RCStatus = cCmdMessageWrite(QueueID, pData, Count); //ERR_MEM here means we must compact the dataspace and retry message write if (RCStatus == ERR_MEM) { cCmdDSCompact(); RCStatus = cCmdMessageWrite(QueueID, pData, Count); } } break; case RC_RESET_POSITION: { i = pInBuf[1]; //Don't do anything if illegal port specification is made if (i >= NO_OF_OUTPUTS) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } //pInBuf[2] is a selector //FALSE: Position relative to start of last program //TRUE: Position relative to start of last motor control block pMapOutPut->Outputs[i].Flags |= (pInBuf[2] ? UPDATE_RESET_BLOCK_COUNT : UPDATE_RESET_ROTATION_COUNT); } break; case RC_GET_BATT_LVL: { if (SendResponse == TRUE) { //Return BatteryVoltage directly from IOMapUI, in mV memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)&(pMapUi->BatteryVoltage), 2); ResponseLen += 2; } } break; case RC_STOP_SOUND: { //Tell sound module to stop playback, no questions asked pMapSound->State = SOUND_STOP; } break; case RC_KEEP_ALIVE: { pMapUi->Flags |= UI_RESET_SLEEP_TIMER; if (SendResponse == TRUE) { //Convert to milliseconds to match external conventions i = (pMapUi->SleepTimeout * 60 * 1000); memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)&i, 4); ResponseLen += 4; } } break; case RC_LS_GET_STATUS: { if (SendResponse == TRUE) { i = pInBuf[1]; //Don't do anything if illegal port specification is made if (i >= NO_OF_LOWSPEED_COM_CHANNEL) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } RCStatus = cCmdLSCheckStatus(i); pOutBuf[ResponseLen] = cCmdLSCalcBytesReady(i); ResponseLen++; } } break; case RC_LS_WRITE: { i = pInBuf[1]; Count = pInBuf[2]; //Don't do anything if illegal port specification is made if (i >= NO_OF_LOWSPEED_COM_CHANNEL) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } RCStatus = cCmdLSWrite(i, Count, &(pInBuf[4]), pInBuf[3]); } break; case RC_LS_READ: { if (SendResponse == TRUE) { i = pInBuf[1]; //Don't do anything if illegal port specification is made if (i >= NO_OF_LOWSPEED_COM_CHANNEL) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } //Get channel status and number of bytes available to read RCStatus = cCmdLSCheckStatus(i); Count = cCmdLSCalcBytesReady(i); pOutBuf[ResponseLen] = (UBYTE)Count; ResponseLen++; //If channel is ready and has data ready for us, put the data into outgoing buffer if (!IS_ERR(RCStatus) && Count > 0) { RCStatus = cCmdLSRead(i, (UBYTE)Count, &(pOutBuf[ResponseLen])); ResponseLen += Count; } //Pad remaining data bytes with zeroes Count = 16 - Count; memset(&(pOutBuf[ResponseLen]), 0, Count); ResponseLen += Count; } } break; case RC_GET_CURR_PROGRAM: { if (SendResponse == TRUE) { //If there's no active program, return error and empty name buffer if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) { RCStatus = ERR_NO_PROG; memset(&(pOutBuf[ResponseLen]), 0, FILENAME_LENGTH + 1); } //Else, copy out stashed program name else { strncpy((PSZ)(&(pOutBuf[ResponseLen])), (PSZ)(VarsCmd.ActiveProgName), FILENAME_LENGTH + 1); } //Regardless, we've copied out a filename's worth of bytes... ResponseLen += FILENAME_LENGTH + 1; } } break; case RC_MESSAGE_READ: { if (SendResponse == TRUE) { QueueID = pInBuf[1]; //Fill in response with remote mailbox number so remote device knows where to store this message. pOutBuf[ResponseLen] = pInBuf[2]; ResponseLen++; RCStatus = cCmdMessageGetSize(QueueID, &Count); pOutBuf[ResponseLen] = Count; ResponseLen++; if (!IS_ERR(RCStatus) && Count > 0) { pData = &(pOutBuf[ResponseLen]); RCStatus = cCmdMessageRead(QueueID, pData, Count, (pInBuf[3])); //If cCmdMessageRead encountered an error, there is no real data in the buffer, so clear it out (below) if (IS_ERR(RCStatus)) Count = 0; else ResponseLen += Count; } //Pad remaining data bytes with zeroes Count = MAX_MESSAGE_SIZE - Count; memset(&(pOutBuf[ResponseLen]), 0, Count); ResponseLen += Count; } } break; // remote-only command to read from datalog buffer // pInBuf[1] = Remove? (bool) case RC_DATALOG_READ: { if (SendResponse == TRUE) { RCStatus = cCmdDatalogGetSize(&Count); pOutBuf[ResponseLen] = Count; ResponseLen++; if (!IS_ERR(RCStatus) && Count > 0) { pData = &(pOutBuf[ResponseLen]); RCStatus = cCmdDatalogRead(pData, Count, (pInBuf[1])); //If cCmdDatalogRead encountered an error, there is no real data in the buffer, so clear it out (below) if (IS_ERR(RCStatus)) Count = 0; else ResponseLen += Count; } //Pad remaining data bytes with zeroes Count = MAX_DATALOG_SIZE - Count; memset(&(pOutBuf[ResponseLen]), 0, Count); ResponseLen += Count; } } break; case RC_DATALOG_SET_TIMES: { //SyncTime SLONG memcpy((PSZ)&IOMapCmd.SyncTime, (PSZ)&(pInBuf[1]), 4); IOMapCmd.SyncTick= dTimerReadNoPoll(); } break; case RC_BT_GET_CONN_COUNT: if (SendResponse == TRUE) { pOutBuf[ResponseLen]= SIZE_OF_BT_CONNECT_TABLE; ResponseLen++; } break; case RC_BT_GET_CONN_NAME: // param in is index, param out is name if (SendResponse == TRUE) { // get index from inbuf i = pInBuf[1]; if(i < SIZE_OF_BT_CONNECT_TABLE) { // unsigned, so guaranteed >= 0 pOutBuf[ResponseLen] = cCmdBTGetDeviceType(pMapComm->BtConnectTable[i].ClassOfDevice); memcpy((PSZ)(&(pOutBuf[ResponseLen+1])), (PSZ)(pMapComm->BtConnectTable[i].Name), SIZE_OF_BT_NAME + 1); ResponseLen += SIZE_OF_BT_NAME + 2; } else { pOutBuf[ResponseLen] = 0; ResponseLen += SIZE_OF_BT_NAME + 2; } } break; case RC_BT_GET_CONTACT_COUNT: if (SendResponse == TRUE) { pOutBuf[ResponseLen]= SIZE_OF_BT_DEVICE_TABLE; ResponseLen++; } break; case RC_BT_GET_CONTACT_NAME: if (SendResponse == TRUE) { // get index from inbuf i = pInBuf[1]; if(i < SIZE_OF_BT_DEVICE_TABLE && (pMapComm->BtDeviceTable[i].DeviceStatus & BT_DEVICE_KNOWN)) { // unsigned, so guaranteed >= 0 (pOutBuf[ResponseLen])= cCmdBTGetDeviceType(pMapComm->BtDeviceTable[i].ClassOfDevice); memcpy((PSZ)(&(pOutBuf[ResponseLen+1])), (PSZ)(pMapComm->BtDeviceTable[i].Name), SIZE_OF_BT_NAME + 1); ResponseLen += SIZE_OF_BT_NAME + 2; } else { pOutBuf[ResponseLen] = 0; memset((PSZ)(&(pOutBuf[ResponseLen+1])), 0, SIZE_OF_BT_NAME + 1); ResponseLen += SIZE_OF_BT_NAME + 2; } } break; case RC_SET_PROPERTY: // label/value pairs i = pInBuf[1]; switch(i) { case RC_PROP_BTONOFF: { UWORD retVal, status; if(pInBuf[2]) status= pMapComm->pFunc(BTON, 0, 0, 0, NULL, &retVal); else status= pMapComm->pFunc(BTOFF, 0, 0, 0, NULL, &retVal); RCStatus= (status == SUCCESS) ? retVal : status; } break; case RC_PROP_SOUND_LEVEL: { UBYTE volume= pInBuf[2]; if(volume > 4) volume= 4; pMapSound->Volume= volume; // apparently stored in two places pMapUi->Volume= volume; } break; case RC_PROP_SLEEP_TIMEOUT: { // ulong millisecs to sleep ULONG value; memcpy((PSZ)&value, (PSZ)&(pInBuf[2]), 4); pMapUi->SleepTimeout= value / 60000; } break; default: //Unknown property -- still inform client to not expect any response bytes NXT_BREAK; RCStatus = ERR_RC_UNKNOWN_CMD; break; } break; case RC_GET_PROPERTY: // label/value pairs if (SendResponse == TRUE) { // get index from inbuf i = pInBuf[1]; switch(i) { case RC_PROP_BTONOFF: pOutBuf[ResponseLen]= pMapUi->BluetoothState != BT_STATE_OFF; ResponseLen++; break; case RC_PROP_SOUND_LEVEL: { pOutBuf[ResponseLen]= pMapSound->Volume; ResponseLen++; } break; case RC_PROP_SLEEP_TIMEOUT: { ULONG value= (pMapUi->SleepTimeout * 60 * 1000); memcpy((PSZ)&(pOutBuf[ResponseLen]), (PSZ)&value, 4); ResponseLen += 4; } break; default: //Unknown property -- still inform client to not expect any response bytes NXT_BREAK; RCStatus = ERR_RC_UNKNOWN_CMD; break; } } break; case RC_UPDATE_RESET_COUNT: { i = pInBuf[1]; //Don't do anything if illegal port specification is made if (i >= NO_OF_OUTPUTS) { RCStatus = ERR_RC_ILLEGAL_VAL; break; } pMapOutPut->Outputs[i].Flags |= UPDATE_RESET_COUNT; } break; default: { //Unknown remote command -- still inform client to not expect any response bytes NXT_BREAK; RCStatus = ERR_RC_UNKNOWN_CMD; } break; }; } //Handle reply telegrams else { switch(pInBuf[0]) { case RC_MESSAGE_READ: { QueueID = pInBuf[2]; Count = pInBuf[3]; pData = &(pInBuf[4]); //This is a response to our request to read a message from a remote mailbox. //If telegram looks valid, write the resulting message into our local mailbox. //(If MsgData is not null-terminated, we can't accept it as a valid string.) if (!IS_ERR((SBYTE)(pInBuf[1])) && Count > 0 && Count <= MAX_MESSAGE_SIZE && pData[Count - 1] == 0x00) { RCStatus = cCmdMessageWrite(QueueID, pData, Count); //ERR_MEM here means we must compact the dataspace if (RCStatus == ERR_MEM) { cCmdDSCompact(); RCStatus = cCmdMessageWrite(QueueID, pData, Count); } } //If telegram doesn't check out, do nothing. No errors are ever returned for reply telegrams. } break; default: { //Unhandled reply telegram. Do nothing. //!!! Could/should stash unhandled/all replies somewhere so a syscall could read them } break; }; } if (SendResponse == TRUE) { //Return response length (pointer checked above) *pLen = (UBYTE)ResponseLen; //Fill in status byte pOutBuf[0] = (UBYTE)(RCStatus); } else *pLen = 0; return (0); } // // Standard interface functions // void cCmdInit(void* pHeader) { ULONG i; pHeaders = pHeader; IOMapCmd.pRCHandler = &cCmdHandleRemoteCommands; #if defined(ARM_DEBUG) //Init run-time assert tracking variables VarsCmd.AssertFlag = FALSE; VarsCmd.AssertLine = 0; #endif //Initialize IO_PTRS_OUT for (i = 0; i < NO_OF_OUTPUTS; i++) { OUTPUT * pOut = &(pMapOutPut->Outputs[i]); IO_PTRS_OUT[IO_OUT_FLAGS + i * IO_OUT_FPP] = (void*)&(pOut->Flags); IO_PTRS_OUT[IO_OUT_MODE + i * IO_OUT_FPP] = (void*)&(pOut->Mode); IO_PTRS_OUT[IO_OUT_SPEED + i * IO_OUT_FPP] = (void*)&(pOut->Speed); IO_PTRS_OUT[IO_OUT_ACTUAL_SPEED + i * IO_OUT_FPP] = (void*)&(pOut->ActualSpeed); IO_PTRS_OUT[IO_OUT_TACH_COUNT + i * IO_OUT_FPP] = (void*)&(pOut->TachoCnt); IO_PTRS_OUT[IO_OUT_TACH_LIMIT + i * IO_OUT_FPP] = (void*)&(pOut->TachoLimit); IO_PTRS_OUT[IO_OUT_RUN_STATE + i * IO_OUT_FPP] = (void*)&(pOut->RunState); IO_PTRS_OUT[IO_OUT_TURN_RATIO + i * IO_OUT_FPP] = (void*)&(pOut->SyncTurnParameter); IO_PTRS_OUT[IO_OUT_REG_MODE + i * IO_OUT_FPP] = (void*)&(pOut->RegMode); IO_PTRS_OUT[IO_OUT_OVERLOAD + i * IO_OUT_FPP] = (void*)&(pOut->Overloaded); IO_PTRS_OUT[IO_OUT_REG_P_VAL + i * IO_OUT_FPP] = (void*)&(pOut->RegPParameter); IO_PTRS_OUT[IO_OUT_REG_I_VAL + i * IO_OUT_FPP] = (void*)&(pOut->RegIParameter); IO_PTRS_OUT[IO_OUT_REG_D_VAL + i * IO_OUT_FPP] = (void*)&(pOut->RegDParameter); IO_PTRS_OUT[IO_OUT_BLOCK_TACH_COUNT + i * IO_OUT_FPP] = (void*)&(pOut->BlockTachoCount); IO_PTRS_OUT[IO_OUT_ROTATION_COUNT + i * IO_OUT_FPP] = (void*)&(pOut->RotationCount); IO_PTRS_OUT[IO_OUT_OPTIONS + i * IO_OUT_FPP] = (void*)&(pOut->Options); IO_PTRS_OUT[IO_OUT_MAX_SPEED + i * IO_OUT_FPP] = (void*)&(pOut->MaxSpeed); IO_PTRS_OUT[IO_OUT_MAX_ACCELERATION + i * IO_OUT_FPP] = (void*)&(pOut->MaxAcceleration); } //Initialize IO_PTRS_IN for (i = 0; i < NO_OF_INPUTS; i++) { INPUTSTRUCT * pIn = &(pMapInput->Inputs[i]); IO_PTRS_IN[IO_IN_TYPE + i * IO_IN_FPP] = (void*)&(pIn->SensorType); IO_PTRS_IN[IO_IN_MODE + i * IO_IN_FPP] = (void*)&(pIn->SensorMode); IO_PTRS_IN[IO_IN_ADRAW + i * IO_IN_FPP] = (void*)&(pIn->ADRaw); IO_PTRS_IN[IO_IN_NORMRAW + i * IO_IN_FPP] = (void*)&(pIn->SensorRaw); IO_PTRS_IN[IO_IN_SCALEDVAL + i * IO_IN_FPP] = (void*)&(pIn->SensorValue); IO_PTRS_IN[IO_IN_INVALID_DATA + i * IO_IN_FPP] = (void*)&(pIn->InvalidData); } //Clear memory pool and initialize VarsCmd (cCmdDeactivateProgram effectively re-inits VarsCmd) cCmdInitPool(); cCmdDeactivateProgram(); //Global state variables for BlueTooth communication. VarsCmd.CommStat = (SWORD)SUCCESS; VarsCmd.CommStatReset = (SWORD)BTBUSY; VarsCmd.CommCurrConnection = 1; //Global flags for various reset and bookkeeping scenarios VarsCmd.DirtyComm = FALSE; VarsCmd.DirtyDisplay = FALSE; VarsCmd.VMState = VM_IDLE; #if defined (ARM_NXT) //Make sure Pool is long-aligned NXT_ASSERT(!((ULONG)(POOL_START) % SIZE_SLONG)); #endif IOMapCmd.ProgStatus = PROG_IDLE; IOMapCmd.ActivateFlag = FALSE; IOMapCmd.Awake = TRUE; //Default offsets explicitly chosen to cause an error if used with IOMAPREAD/IOMAPWRITE //Real values will be set when programs run and/or the DS is re-arranged. IOMapCmd.OffsetDVA = 0xFFFF; IOMapCmd.OffsetDS = 0xFFFF; //Initialize format string and clear out FileName string strncpy((PSZ)(IOMapCmd.FormatString), VM_FORMAT_STRING, VM_FORMAT_STRING_SIZE); memset(IOMapCmd.FileName, 0, sizeof(IOMapCmd.FileName)); dTimerInit(); IOMapCmd.Tick = dTimerRead(); IOMapCmd.SyncTime= 0; IOMapCmd.SyncTick= 0; return; } void cCmdCtrl(void) { NXT_STATUS Status = NO_ERR; switch (VarsCmd.VMState) { case VM_RUN_FREE: case VM_RUN_SINGLE: { #if VMProfilingCode ULONG EnterTime= dTimerReadHiRes(), FinishTime; CmdCtrlCalls ++; #endif ULONG Continue; #if VM_BENCHMARK //IOMapCmd.Tick currently holds the tick from the end of last cCmdCtrl call. //If we don't come back here before dTimerRead() increments, the m_sched loop has taken *at least* 1 ms. if (IOMapCmd.Tick != dTimerRead()) { VarsCmd.OverTimeCount++; //Record maximum magnitude of schedule loop overage, in millisecs if (dTimerRead() - IOMapCmd.Tick > VarsCmd.MaxOverTimeLength) VarsCmd.MaxOverTimeLength = dTimerRead() - IOMapCmd.Tick; } VarsCmd.CmdCtrlCount++; #endif //Abort current program if cancel button is pressed if (IOMapCmd.DeactivateFlag == TRUE || pMapButton->State[BTN1] & PRESSED_EV) { IOMapCmd.DeactivateFlag = FALSE; //Clear pressed event so it doesn't get double-counted by UI pMapButton->State[BTN1] &= ~PRESSED_EV; //Go to VM_RESET1 state and report abort VarsCmd.VMState = VM_RESET1; IOMapCmd.ProgStatus = PROG_ABORT; break; } //Assert that we have an active program NXT_ASSERT(VarsCmd.ActiveProgHandle != NOT_A_HANDLE); //Handle any resting clumps that are ready to awaken cCmdCheckRestQ(IOMapCmd.Tick); // not using result, yet //Execute from at least one clump do { //Execute instructions from a clump up to INSTR_MAX, to end of millisec, //Finishing/suspending a clump, BREAKOUT_REQ, or any errors will cause a return #if VMProfilingCode ULONG ClumpEnterTime= dTimerReadHiRes(); CLUMP_ID clump= VarsCmd.RunQ.Head; #endif Status = cCmdInterpFromClump(); #if VMProfilingCode CmdCtrlClumpTime[clump] += dTimerReadHiRes() - ClumpEnterTime; #endif //If RunQ and RestQ are empty, program is done, or wacko if (!cCmdIsClumpIDSane(VarsCmd.RunQ.Head)) { Continue = FALSE; if(!cCmdIsClumpIDSane(VarsCmd.RestQ.Head)) { VarsCmd.VMState = VM_RESET1; IOMapCmd.ProgStatus = PROG_OK; } } else if (Status == CLUMP_SUSPEND || Status == CLUMP_DONE) Continue = TRUE; // queue isn't empty, didn't timeout //Only rotate RunQ on a "normal" finish, i.e. no error, clump end, or breakout request else if (Status == ROTATE_QUEUE) { // done and suspend do their own cCmdRotateQ(); Continue= TRUE; } else if (Status == TIMES_UP) { cCmdRotateQ(); Continue = FALSE; } else if (IS_ERR(Status)) // mem error is handled in InterpFromClump if possible { Continue = FALSE; VarsCmd.VMState = VM_RESET1; IOMapCmd.ProgStatus = PROG_ERROR; } else if (Status == STOP_REQ) { Continue = FALSE; VarsCmd.VMState = VM_RESET1; IOMapCmd.ProgStatus = PROG_OK; } else if (Status == BREAKOUT_REQ) { Continue = FALSE; } } while (Continue == TRUE); #if VMProfilingCode FinishTime= dTimerReadHiRes(); if(NotFirstCall) OverheadTime += EnterTime - LeaveTime; else NotFirstCall= 1; CmdCtrlTime += FinishTime - EnterTime; LeaveTime= FinishTime; #endif // May busy wait to postpone to 1ms schedule while (IOMapCmd.Tick == dTimerRead()); } break; case VM_IDLE: { //If there's a new program to activate... if (IOMapCmd.ActivateFlag == TRUE) { //Clear flag so we only activate once per new file IOMapCmd.ActivateFlag = FALSE; Status = cCmdActivateProgram(IOMapCmd.FileName); //If we hit an activation error: //1. Set PROG_ERROR status //2. Proceed to VM_RESET1 (some unneeded work, yes, but preserves contract with UI if (IS_ERR(Status)) { IOMapCmd.ProgStatus = PROG_ERROR; VarsCmd.VMState = VM_RESET1; } //Else start running program else { VarsCmd.VMState = VM_RUN_FREE; IOMapCmd.ProgStatus = PROG_RUNNING; VarsCmd.StartTick = IOMapCmd.Tick; if(VarsCmd.VMState == VM_RUN_FREE) gInstrsToExecute = 20; else gInstrsToExecute= 1; #if VM_BENCHMARK //Re-init benchmark VarsCmd.InstrCount = 0; VarsCmd.Average = 0; VarsCmd.OverTimeCount = 0; VarsCmd.MaxOverTimeLength = 0; VarsCmd.CmdCtrlCount = 0; VarsCmd.CompactionCount = 0; VarsCmd.LastCompactionTick = 0; VarsCmd.MaxCompactionTime = 0; memset(VarsCmd.OpcodeBenchmarks, 0, sizeof(VarsCmd.OpcodeBenchmarks)); memset(VarsCmd.SyscallBenchmarks, 0, sizeof(VarsCmd.SyscallBenchmarks)); #endif //Reset devices to a known state before we begin running cCmdResetDevices(); pMapUi->Flags |= (UI_DISABLE_LEFT_RIGHT_ENTER | UI_DISABLE_EXIT); } } while (IOMapCmd.Tick == dTimerRead()); // delay until scheduled time } break; //Initialize VM internal state data and devices which must respond immediately to program ending case VM_RESET1: { //If we aborted a program, reset devices (specifically, motors) immediately //Otherwise, wait for UI to put us into PROG_RESET (gives motors a chance to brake before setting to coast) //!!! This means cCmdResetDevices will get called twice on abort. Should not be a big deal. if (IOMapCmd.ProgStatus == PROG_ABORT) cCmdResetDevices(); //Reenable UI access to buttons pMapUi->Flags &= ~(UI_DISABLE_LEFT_RIGHT_ENTER | UI_DISABLE_EXIT); #if VM_BENCHMARK if (IOMapCmd.Tick != VarsCmd.StartTick) VarsCmd.Average = VarsCmd.InstrCount / (IOMapCmd.Tick - VarsCmd.StartTick); else //It appears that we finished in 0 milliseconds. Very unlikely on ARM, so set a flag value. VarsCmd.Average = 0xFFFFFFFF; cCmdWriteBenchmarkFile(); #endif //Re-initialize program state data (contents of memory pool preserved) //!!! Skip this step in simulator builds so helper access methods still work #ifndef SIM_NXT cCmdDeactivateProgram(); #endif //SIM_NXT //If this program has taken over the display, reset it for the UI cCmdRestoreDefaultScreen(); //Stop any currently playing sound and re-init volume according to UI prefs pMapSound->State = SOUND_STOP; pMapSound->Volume = pMapUi->Volume; //Artificially set CommStatReset to BTBUSY to force at least one SETCMDMODE call (see VM_RESET2 case) VarsCmd.CommStatReset = (SWORD)BTBUSY; VarsCmd.VMState = VM_RESET2; while (IOMapCmd.Tick == dTimerRead()); // delay until scheduled time } break; case VM_RESET2: { //Reset BlueCore into "command mode" (close any open streams) //Since SETCMDMODE subject to BTBUSY, we may need to make multiple calls //Any CommStatReset value other than BTBUSY means our request was accepted //Assumptions: //Process should never take longer than UI timeout (see below), but if it does, // we could be left with the stream open to an NXT peer and block out the PC. //Also assuming that once SETCMDMODE request is accepted, it never fails. if (VarsCmd.CommStatReset == (SWORD)BTBUSY && VarsCmd.DirtyComm == TRUE) pMapComm->pFunc(SETCMDMODE, 0, 0, 0, NULL, (UWORD*)&(VarsCmd.CommStatReset)); //If UI is done displaying ending program status, move on. if (IOMapCmd.ProgStatus == PROG_RESET) { //Reset devices whenever a program ends for any reason cCmdResetDevices(); VarsCmd.DirtyComm = FALSE; //Go to VM_IDLE state VarsCmd.VMState = VM_IDLE; IOMapCmd.ProgStatus = PROG_IDLE; } while (IOMapCmd.Tick == dTimerRead()); // delay until scheduled time } break; }//END state machine switch //Set tick to new value for next time 'round IOMapCmd.Tick = dTimerReadNoPoll(); return; } void cCmdExit(void) { dTimerExit(); return; } NXT_STATUS cCmdReadFileHeader(UBYTE* pData, ULONG DataSize, PROG_FILE_OFFSETS* pFileOffsets) { ULONG i; UBYTE * pCursor; UWORD CurrOffset = 0; UBYTE DepCount; UWORD DopeVectorOffset; UWORD FileClumpCount; UBYTE FileMajor, FileMinor, CompatibleMinor, CompatibleMajor, CurrentMajor; NXT_ASSERT(pData != NULL); if (strncmp((PSZ)pData, "NXTBINARY", VM_FORMAT_STRING_SIZE) == 0) { ULONG NativeOffset; pCursor = (pData + 12); NativeOffset = (ULONG)(*pCursor); void (*native)(ULONG, ULONG) = (void (*)())(pData + NativeOffset); (*native)((ULONG)pData, DataSize); NXT_BREAK; return (ERR_VER); } //Assign pCursor to point to version word inside file header pCursor = (pData + VM_FORMAT_STRING_SIZE - 2); //Decode version numbers into comparable bytes FileMajor = *pCursor; FileMinor = *(pCursor + 1); CompatibleMajor = (UBYTE)(VM_OLDEST_COMPATIBLE_VERSION >> 8); CompatibleMinor = (UBYTE)(VM_OLDEST_COMPATIBLE_VERSION); CurrentMajor = (UBYTE)(FIRMWAREVERSION >> 8); //CurrentMinor = (UBYTE)(FIRMWAREVERSION); //Return ERR_VER if file lacks proper format string or version number //!!! Only checking major version recommended for future development if (strncmp((PSZ)pData, VM_FORMAT_STRING, VM_FORMAT_STRING_SIZE) || FileMajor < CompatibleMajor || FileMinor < CompatibleMinor || FileMajor > CurrentMajor) { NXT_BREAK; return (ERR_VER); } //Advance CurrOffset past header information CurrOffset += VM_FORMAT_STRING_SIZE; // //Initialize bookkeeping variables // VarsCmd.DataspaceCount = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; VarsCmd.DataspaceSize = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; VarsCmd.DSStaticSize = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; pFileOffsets->DSDefaultsSize = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; pFileOffsets->DynamicDefaults = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; pFileOffsets->DynamicDefaultsSize = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; VarsCmd.MemMgr.Head = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; VarsCmd.MemMgr.Tail = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; DopeVectorOffset = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; //!!! Odd code here to deal with type mismatch between file format and CLUMP_ID typedef. //Neither is trivial to change, so it's best to just check the data for consistency here. FileClumpCount = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; //Must have at least one clump and count can't exceed the NOT_A_CLUMP sentinel if (FileClumpCount == 0 || FileClumpCount >= NOT_A_CLUMP) return (ERR_FILE); else VarsCmd.AllClumpsCount = (CLUMP_ID)FileClumpCount; VarsCmd.CodespaceCount = *((UWORD*)(pData + CurrOffset)); CurrOffset += 2; //Can't have a valid program with no code if (VarsCmd.CodespaceCount == 0) return (ERR_FILE); // // Now, calculate offsets for each data segment in the file // CurrOffset += CurrOffset % 2; pFileOffsets->DSTOC = CurrOffset; CurrOffset += VarsCmd.DataspaceCount * sizeof(DS_TOC_ENTRY); CurrOffset += CurrOffset % 2; pFileOffsets->DSDefaults = CurrOffset; CurrOffset += pFileOffsets->DSDefaultsSize; //ClumpRecs must be aligned on even boundaries CurrOffset += CurrOffset % 2; pFileOffsets->Clumps = CurrOffset; //Set cursor to start of clump records pCursor = pData + CurrOffset; //Set CurrOffset to start of dependent lists CurrOffset += VarsCmd.AllClumpsCount * VM_FILE_CLUMP_REC_SIZE; //Read dependent count from each clump record, advancing CurrOffset accordingly for (i = 0; i < VarsCmd.AllClumpsCount; i++) { DepCount = *(pCursor + 1); CurrOffset += DepCount; pCursor += VM_FILE_CLUMP_REC_SIZE; } //Codespace must be aligned on even boundary CurrOffset += CurrOffset % 2; pFileOffsets->Codespace = CurrOffset; //No need to read through codespace, but make sure CurrOffset ended up sane //If not, something went wrong reading the header information if (CurrOffset != (DataSize - VarsCmd.CodespaceCount * 2)) { NXT_BREAK; return (ERR_FILE); } // // Finally, update VarsCmd fields // VarsCmd.RunQ.Head = NOT_A_CLUMP; VarsCmd.RunQ.Tail = NOT_A_CLUMP; VarsCmd.RestQ.Head = NOT_A_CLUMP; VarsCmd.RestQ.Tail = NOT_A_CLUMP; //Reset codespace pointer VarsCmd.pCodespace = (CODE_WORD*)(pData + pFileOffsets->Codespace); //...placing clump records first... VarsCmd.pAllClumps = (CLUMP_REC*)(VarsCmd.Pool + VarsCmd.PoolSize); VarsCmd.PoolSize += VarsCmd.AllClumpsCount * sizeof(CLUMP_REC); //...then DSTOC... VarsCmd.pDataspaceTOC = (DS_TOC_ENTRY*)(pData + pFileOffsets->DSTOC); //...then the dataspace itself ALIGN_TO_MOD(VarsCmd.PoolSize, POOL_ALIGN); VarsCmd.pDataspace = (VarsCmd.Pool + VarsCmd.PoolSize); IOMapCmd.OffsetDS = (UWORD)((ULONG)(VarsCmd.pDataspace) - (ULONG)&(IOMapCmd)); VarsCmd.PoolSize += VarsCmd.DataspaceSize; //init rest of MemMgr VarsCmd.MemMgr.pDopeVectorArray = (DOPE_VECTOR *)(VarsCmd.pDataspace + DopeVectorOffset); IOMapCmd.OffsetDVA = (UWORD)((ULONG)(VarsCmd.MemMgr.pDopeVectorArray) - (ULONG)&(IOMapCmd)); VarsCmd.MemMgr.FreeHead = NOT_A_DS_ID; if (VarsCmd.PoolSize > POOL_MAX_SIZE) { NXT_BREAK; return (ERR_FILE); } return (NO_ERR); } //!!! Recursive function NXT_STATUS cCmdInflateDSDefaults(UBYTE* pDSDefaults, UWORD *pDefaultsOffset, DS_ELEMENT_ID DSElementID) { NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode; UWORD i, Count; UBYTE *pVal; NXT_ASSERT(cCmdIsDSElementIDSane(DSElementID)); TypeCode = cCmdDSType(DSElementID); if (TypeCode > TC_LAST_VALID) return ERR_INSTR; else if (TypeCode == TC_CLUSTER) { Count = cCmdClusterCount(DSElementID); //Advance DSElementID to sub-type DSElementID = INC_ID(DSElementID); //Loop through sub-types, inflate recursively for (i = 0; i < Count; i++) { Status = cCmdInflateDSDefaults(pDSDefaults, pDefaultsOffset, DSElementID); if (IS_ERR(Status)) return Status; DSElementID = cCmdNextDSElement(DSElementID); } } else { if (TypeCode == TC_ARRAY) { //Resolve pointer to DVIndex pVal = VarsCmd.pDataspace + VarsCmd.pDataspaceTOC[DSElementID].DSOffset; } else { pVal = cCmdResolveDataArg(DSElementID, 0, NULL); } //Check if the element has the "default default" if (VarsCmd.pDataspaceTOC[DSElementID].Flags & DS_DEFAULT_DEFAULT) { //Fill element with the "default default" of zero memset(pVal, 0, cCmdSizeOf(TypeCode)); } else { //Get default from stream memmove(pVal, pDSDefaults + *pDefaultsOffset, cCmdSizeOf(TypeCode)); *pDefaultsOffset += cCmdSizeOf(TypeCode); } } //!!! Currently will always return NO_ERR return Status; } void cCmdRefreshActiveClump(CLUMP_ID CurrID) { CLUMP_REC * clumpRecPtr= &(VarsCmd.pAllClumps[CurrID]); if(clumpRecPtr->clumpScalarDispatchHints & scalarBinopDispatchMask) InterpFuncs[8]= cCmdInterpScalarBinop; else InterpFuncs[8]= cCmdInterpBinop; if(clumpRecPtr->clumpScalarDispatchHints & scalarUnop2DispatchMask) InterpFuncs[6]= cCmdInterpScalarUnop2; else InterpFuncs[6]= cCmdInterpUnop2; } NXT_STATUS cCmdActivateProgram(UBYTE * pFileName) { UWORD i, j; UBYTE * pCursor; NXT_STATUS Status = NO_ERR; PROG_FILE_OFFSETS FileOffsets; LOADER_STATUS LStatus; ULONG DataSize; UBYTE * pData; ULONG pDataHolder; UWORD DefaultsOffset; LStatus = pMapLoader->pFunc(OPENREADLINEAR, pFileName, (UBYTE*)(&pDataHolder), &DataSize); pData = (UBYTE*)(pDataHolder); //If Loader returned error or bad file pointer, bail out if (LOADER_ERR(LStatus) != SUCCESS || pData == NULL || DataSize == 0) return (ERR_FILE); //Deactivate current program and re-initialize memory pool cCmdDeactivateProgram(); cCmdInitPool(); //Stash this program's handle since we hold it open while running VarsCmd.ActiveProgHandle = LOADER_HANDLE(LStatus); //Stash this program's name for easy reference later strncpy((PSZ)(VarsCmd.ActiveProgName), (PSZ)(pFileName), FILENAME_LENGTH + 1); //Consume activation record data stream. //See TargettingVIs/NXT.PackAR.vi for data stream packing details //Read header portion of the file, calculating offsets and initializing VarsCmd Status = cCmdReadFileHeader(pData, DataSize, &FileOffsets); if (IS_ERR(Status)) return Status; //Do some spot checks to make sure bad file contents didn't leave us with obviously insane VarsCmd contents //!!! Should add alignment checks on these pointers to avoid data abort exceptions later if (((UBYTE*)(VarsCmd.pCodespace) < pData) || ((UBYTE*)(VarsCmd.pCodespace) >= (pData + DataSize)) || ((UBYTE*)(VarsCmd.pAllClumps) < POOL_START) || ((UBYTE*)(VarsCmd.pAllClumps) >= POOL_SENTINEL) || ((UBYTE*)(VarsCmd.pDataspace) < POOL_START) || ((UBYTE*)(VarsCmd.pDataspace) >= POOL_SENTINEL) || (VarsCmd.DataspaceSize == 0) ) { NXT_BREAK; return ERR_FILE; } //Initialize CLUMP_RECs as contiguous list in RAM pCursor = (pData + FileOffsets.Clumps); for (i = 0; i < VarsCmd.AllClumpsCount; i++) { CLUMP_REC *clumpPtr= &VarsCmd.pAllClumps[i]; clumpPtr->InitFireCount = *(UBYTE*)(pCursor + i * VM_FILE_CLUMP_REC_SIZE); clumpPtr->DependentCount = *(UBYTE*)(pCursor + (i * VM_FILE_CLUMP_REC_SIZE) + 1); clumpPtr->CodeStart = *(UWORD*)(pCursor + (i * VM_FILE_CLUMP_REC_SIZE) + 2) + VarsCmd.pCodespace; //Initialize remaining CLUMP_REC fields clumpPtr->PC = clumpPtr->CodeStart; clumpPtr->Link = NOT_A_CLUMP; //Activate any clumps with CurrFireCount of 0 clumpPtr->CurrFireCount = clumpPtr->InitFireCount; if (clumpPtr->CurrFireCount == 0) cCmdEnQClump(&(VarsCmd.RunQ), (CLUMP_ID)i); } //Patch up dependents in separate pass (reuse of pCursor) pCursor += VarsCmd.AllClumpsCount * VM_FILE_CLUMP_REC_SIZE; for (i = 0; i < VarsCmd.AllClumpsCount; i++) { CLUMP_REC *clumpPtr= &VarsCmd.pAllClumps[i]; if (clumpPtr->DependentCount > 0) { clumpPtr->pDependents = (CLUMP_ID*)(pCursor); pCursor += (clumpPtr->DependentCount * sizeof(CLUMP_ID)); } else clumpPtr->pDependents = NULL; //Patch up CodeEnd value based on CodeStart of next clump or last overall codeword if (i < (VarsCmd.AllClumpsCount - 1)) clumpPtr->CodeEnd = (clumpPtr+1)->CodeStart - 1; else clumpPtr->CodeEnd = VarsCmd.CodespaceCount - 1 + VarsCmd.pCodespace; //Test for empty/insane clump code definitions NXT_ASSERT(clumpPtr->CodeStart < clumpPtr->CodeEnd); } // Check if the instructions within a clump are polymorphic and mark which table to dispatch from for (i = 0; i < VarsCmd.AllClumpsCount; i++) { // Check type on Boolean, math, ArrInit and ArrIndex, ingore GetSet I/O as these are always scalar // do we need to check for DataArg encodings to I/O map??? GM // Get Opcode and size of each instr, if ^^, check Arg types for Array or Cluster CLUMP_REC *clumpPtr= &VarsCmd.pAllClumps[i]; CODE_WORD *pInstr = clumpPtr->CodeStart, *lastPC = clumpPtr->CodeEnd; ULONG InstrSize, opCode, shortOp, isT2Agg, isT3Agg, isScalarBinop= TRUE, isScalarUnop2= TRUE; TYPE_CODE t1, t2, t3; ULONG instrWord; do { instrWord= *(UWORD*)pInstr; opCode= OP_CODE(pInstr); shortOp= (instrWord>>8) & 0x0F; InstrSize = INSTR_SIZE(instrWord); if (InstrSize == VAR_INSTR_SIZE) InstrSize = ((UWORD*)pInstr)[1]; if(shortOp <= 7) // no shorts are binOps { t2= cCmdDSType(pInstr[2]); isT2Agg= IS_AGGREGATE_TYPE(t2); if(InstrSize == 8) { t3= cCmdDSType(pInstr[3]); isT3Agg= IS_AGGREGATE_TYPE(t3); if(isT2Agg || isT3Agg) { if(opCode == OP_CMP) { UBYTE isString2, isString3; isString2= (t2 == TC_ARRAY) && cCmdDSType(INC_ID(pInstr[2])) == TC_UBYTE; isString3= (t3 == TC_ARRAY) && cCmdDSType(INC_ID(pInstr[3])) == TC_UBYTE; t1= cCmdDSType(pInstr[1]); if((!isString2 || !isString3) || t1 == TC_ARRAY) // allow strings to go scalar, don't let through element compares of bytes or Bools isScalarBinop= FALSE; } else if(opCode == OP_BRCMP) isScalarBinop= FALSE; } } else if(InstrSize == 6 && isT2Agg && (opCode == OP_NOT || opCode == OP_BRTST)) isScalarUnop2= FALSE; } pInstr += InstrSize/2; } while((isScalarBinop || isScalarUnop2) && pInstr < lastPC); if(isScalarBinop) clumpPtr->clumpScalarDispatchHints |= scalarBinopDispatchMask; else clumpPtr->clumpScalarDispatchHints &= ~scalarBinopDispatchMask; if(isScalarUnop2) clumpPtr->clumpScalarDispatchHints |= scalarUnop2DispatchMask; else clumpPtr->clumpScalarDispatchHints &= ~scalarUnop2DispatchMask; } //Programs with no active clumps constitutes an activation error if (VarsCmd.RunQ.Head == NOT_A_CLUMP) return (ERR_FILE); else { // now that we know which clumps are scalar and poly, refresh dispatch table to match head cCmdRefreshActiveClump(VarsCmd.RunQ.Head); } //Initialize dataspace with default values from file //!!! This would be a good place to enforce check against potentially // unsafe nested types (deeply nested types mean deep recursive calls) DefaultsOffset = 0; for (i = 0; i != NOT_A_DS_ID; i = cCmdNextDSElement(i)) { Status = cCmdInflateDSDefaults(pData + FileOffsets.DSDefaults, &DefaultsOffset, i); if (IS_ERR(Status)) return Status; } if ((DefaultsOffset != FileOffsets.DynamicDefaults) || (DefaultsOffset + FileOffsets.DynamicDefaultsSize != FileOffsets.DSDefaultsSize)) { NXT_BREAK; return (ERR_FILE); } //Copy Dynamic defaults from file memmove(VarsCmd.pDataspace + VarsCmd.DSStaticSize, pData + FileOffsets.DSDefaults + FileOffsets.DynamicDefaults, FileOffsets.DynamicDefaultsSize); // fix memmgr links. old files contain unused backPtrs, we now use these to store backLink DV_INDEX prev= NOT_A_DS_ID; for (i = VarsCmd.MemMgr.Head; i != NOT_A_DS_ID; i = DV_ARRAY[i].Link) { DV_ARRAY[i].BackLink= prev; prev= i; } //Verify the MemMgr ended up where we said it would if ((UBYTE *)VarsCmd.MemMgr.pDopeVectorArray != VarsCmd.pDataspace + DV_ARRAY[0].Offset) { NXT_BREAK; return (ERR_FILE); } //Initialize message queues for (i = 0; i < MESSAGE_QUEUE_COUNT; i++) { VarsCmd.MessageQueues[i].ReadIndex = 0; VarsCmd.MessageQueues[i].WriteIndex = 0; for (j = 0; j < MESSAGES_PER_QUEUE; j++) { VarsCmd.MessageQueues[i].Messages[j] = NOT_A_DS_ID; } } //Initialize datalog queue VarsCmd.DatalogBuffer.ReadIndex = 0; VarsCmd.DatalogBuffer.WriteIndex = 0; for (j = 0; j < DATALOG_QUEUE_DEPTH; j++) { VarsCmd.DatalogBuffer.Datalogs[j] = NOT_A_DS_ID; } // now that we've loaded program, prime memmgr dopevectors based upon number of handles in ds. ULONG numHandles= DV_ARRAY[0].Count/2; if(numHandles > 200) numHandles= 200; Status = cCmdGrowDopeVectorArray(numHandles); if (cCmdVerifyMemMgr() != TRUE) return (ERR_FILE); gUsageSemData= 0; gRequestSemData= 0; // preload all calibration coefficients into mem cCmdLoadCalibrationFiles(); return (Status); } void cCmdDeactivateProgram() { UBYTE i, tmp; //Wipe away all references into the pool and clear all run-time data VarsCmd.pCodespace = NULL; VarsCmd.CodespaceCount = 0; VarsCmd.pAllClumps = NULL; VarsCmd.AllClumpsCount = 0; VarsCmd.DataspaceCount = 0; VarsCmd.pDataspaceTOC = NULL; VarsCmd.pDataspace = NULL; VarsCmd.DataspaceSize = 0; VarsCmd.DSStaticSize = 0; VarsCmd.MemMgr.Head = NOT_A_DS_ID; VarsCmd.MemMgr.Tail = NOT_A_DS_ID; VarsCmd.MemMgr.FreeHead = NOT_A_DS_ID; VarsCmd.MemMgr.pDopeVectorArray = NULL; VarsCmd.RunQ.Head = NOT_A_CLUMP; VarsCmd.RunQ.Tail = NOT_A_CLUMP; if (VarsCmd.ActiveProgHandle != NOT_A_HANDLE) { //Close handle that we've kept open for this program pMapLoader->pFunc(CLOSE, &(VarsCmd.ActiveProgHandle), NULL, NULL); VarsCmd.ActiveProgHandle = NOT_A_HANDLE; //Clear internal stashed name memset(VarsCmd.ActiveProgName, 0, FILENAME_LENGTH + 1); } //Close any files we had opened programatically for (i = 0; i < MAX_HANDLES; i++) { //Copy i to tmp, because we pass a pointer to it to pFunc tmp = i; //Close file if (*(VarsCmd.FileHandleTable[i]) != 0) pMapLoader->pFunc(CROPDATAFILE, &tmp, NULL, NULL); } //Clear FileHandleTable memset(VarsCmd.FileHandleTable, 0, sizeof(VarsCmd.FileHandleTable)); return; } void cCmdResetDevices(void) { UBYTE i; //Clear NXT button counts so 'bumped' will work on first run for (i = 0; i < NO_OF_BTNS; i++) { pMapButton->BtnCnt[i].RelCnt = 0; //Need to clear short and long counts too, because RelCnt depends on them. No known side effects. pMapButton->BtnCnt[i].ShortRelCnt = 0; pMapButton->BtnCnt[i].LongRelCnt = 0; } for (i = 0; i < NO_OF_INPUTS; i++) { INPUTSTRUCT * pIn = &(pMapInput->Inputs[i]); //Clear type and mode to defaults pIn->SensorType = NO_SENSOR; pIn->SensorMode = RAWMODE; //Reset input values to 0 prior to running (clear things like stale rotation counts) pIn->ADRaw = 0; pIn->SensorRaw = 0; pIn->SensorValue = 0; //Assert invalid data flag so future code is aware of these changes pIn->InvalidData = TRUE; } for (i = 0; i < NO_OF_OUTPUTS; i++) { //Coast and reset all motor parameters OUTPUT * pOut = &(pMapOutPut->Outputs[i]); pOut->Mode = 0; pOut->RegMode = REGULATION_MODE_IDLE; pOut->RunState = MOTOR_RUN_STATE_IDLE; pOut->Speed = 0; pOut->TachoLimit = 0; pOut->SyncTurnParameter = 0; pOut->Flags = UPDATE_MODE | UPDATE_SPEED | UPDATE_TACHO_LIMIT | UPDATE_RESET_COUNT | UPDATE_RESET_BLOCK_COUNT | UPDATE_RESET_ROTATION_COUNT; } //Lowspeed init, INSERT CODE !!! for (i = 0; i < NO_OF_LOWSPEED_COM_CHANNEL; i++) { pMapLowSpeed->InBuf[i].InPtr = 0; pMapLowSpeed->InBuf[i].OutPtr = 0; pMapLowSpeed->InBuf[i].BytesToRx = 0; pMapLowSpeed->OutBuf[i].InPtr = 0; pMapLowSpeed->OutBuf[i].OutPtr = 0; if (pMapLowSpeed->ChannelState[i] != LOWSPEED_IDLE) { pMapLowSpeed->ChannelState[i] = LOWSPEED_DONE; pMapLowSpeed->State |= (0x01<Head == NOT_A_CLUMP) { NXT_ASSERT(Queue->Tail == NOT_A_CLUMP); Queue->Head = NewClump; Queue->Tail = NewClump; if(Queue == &(VarsCmd.RunQ)) cCmdRefreshActiveClump(NewClump); } //Otherwise, tack onto the end else { VarsCmd.pAllClumps[Queue->Tail].Link = NewClump; Queue->Tail = NewClump; } return; } //Dequeue specified clump //Normal usage is to dequeue only from the head (i.e. pass Queue.Head as arg) void cCmdDeQClump(CLUMP_Q * Queue, CLUMP_ID Clump) { CLUMP_ID CurrID, LinkID; //Make sure Clump's ID is valid and is already on Queue NXT_ASSERT(cCmdIsClumpIDSane(Clump)); NXT_ASSERT(cCmdIsQSane(Queue) == TRUE); NXT_ASSERT(cCmdIsClumpOnQ(Queue, Clump)); CurrID = Queue->Head; //If our clump is the head, move up the next and disconnect if (CurrID == Clump) { Queue->Head = VarsCmd.pAllClumps[Clump].Link; VarsCmd.pAllClumps[Clump].Link = NOT_A_CLUMP; //If we just removed the last clump, patch up the queue's tail if (Queue->Head == NOT_A_CLUMP) Queue->Tail = NOT_A_CLUMP; else if(Queue == &(VarsCmd.RunQ)) cCmdRefreshActiveClump(Queue->Head); } //Else, look through rest of list looking for a link to our clump else { do { CLUMP_REC *clumpPtr= &VarsCmd.pAllClumps[CurrID]; LinkID = clumpPtr->Link; //If we find a link to our clump, patch up predecessor's link if (clumpPtr->Link == Clump) { clumpPtr->Link = VarsCmd.pAllClumps[Clump].Link; VarsCmd.pAllClumps[Clump].Link = NOT_A_CLUMP; //If we just removed the tail, patch tail if (Clump == Queue->Tail) Queue->Tail = CurrID; } CurrID = LinkID; } while (CurrID != NOT_A_CLUMP); } return; } //Rotate head to tail and advance head for given Queue void cCmdRotateQ() { CLUMP_ID CurrID; CLUMP_REC * pClumpRec; CLUMP_Q * Queue = &VarsCmd.RunQ; //Make sure Queue is sane NXT_ASSERT(cCmdIsQSane(Queue) == TRUE); //If queue has at least two clumps if (Queue->Head != Queue->Tail) { CurrID = Queue->Head; pClumpRec = &(VarsCmd.pAllClumps[CurrID]); //Disconnect head Queue->Head = pClumpRec->Link; pClumpRec->Link = NOT_A_CLUMP; //Reconnect head as tail pClumpRec = &(VarsCmd.pAllClumps[Queue->Tail]); pClumpRec->Link = CurrID; Queue->Tail = CurrID; // reinit clump info CurrID= Queue->Head; cCmdRefreshActiveClump(Queue->Head); //Make sure we didn't make any really stupid mistakes NXT_ASSERT(cCmdIsQSane(Queue) == TRUE); } return; } UBYTE cCmdIsClumpOnQ(CLUMP_Q * Queue, CLUMP_ID Clump) { CLUMP_ID CurrID; //Make sure Clump's ID is valid and is already on Queue NXT_ASSERT(cCmdIsClumpIDSane(Clump)); NXT_ASSERT(cCmdIsQSane(Queue) == TRUE); CurrID = Queue->Head; while (CurrID != NOT_A_CLUMP) { if (CurrID == Clump) return TRUE; CurrID = VarsCmd.pAllClumps[CurrID].Link; } return FALSE; } UBYTE cCmdIsQSane(CLUMP_Q * Queue) { CLUMP_ID Head, Tail; CLUMP_REC * pHead; if (Queue == NULL) { NXT_BREAK; return FALSE; } Head = Queue->Head; Tail = Queue->Tail; if (Head == NOT_A_CLUMP && cCmdIsClumpIDSane(Tail)) return FALSE; if (cCmdIsClumpIDSane(Head) && Tail == NOT_A_CLUMP) return FALSE; if (cCmdIsClumpIDSane(Head) && cCmdIsClumpIDSane(Tail)) { pHead = &(VarsCmd.pAllClumps[Head]); //!!! More comprehensive queue tests could go here //Check for mislinked head if there are at least two queue members if (Head != Tail && pHead->Link == NOT_A_CLUMP) return FALSE; } return TRUE; } // // Mutex queuing functions // NXT_STATUS cCmdAcquireMutex(MUTEX_Q * Mutex) { NXT_STATUS Status = NO_ERR; CLUMP_ID Clump= VarsCmd.RunQ.Head; // save off before queue changes below NXT_ASSERT(Mutex != NULL && cCmdIsClumpIDSane(Clump)); if (Mutex->Owner == NOT_A_CLUMP) { //Mutex is open, so just take it Mutex->Owner = Clump; NXT_ASSERT(Mutex->WaitQ.Head == NOT_A_CLUMP && Mutex->WaitQ.Tail == NOT_A_CLUMP); } else { //Mutex is reserved by someone else, take self off RunQ and add to WaitQ cCmdDeQClump(&(VarsCmd.RunQ), Clump); cCmdEnQClump(&(Mutex->WaitQ), Clump); Status = CLUMP_SUSPEND; } NXT_ASSERT(cCmdIsQSane(&(Mutex->WaitQ))); return (Status); } NXT_STATUS cCmdReleaseMutex(MUTEX_Q * Mutex) { #if defined(WIN_DEBUG) || defined(ARM_DEBUG) CLUMP_ID Clump= VarsCmd.RunQ.Head; #endif NXT_ASSERT(Mutex != NULL); //!!! don't actually need to pass in Owner clump, but provides nice error checking for now // Might want to return an error/warning if we see a Release on an free mutex, though... NXT_ASSERT(Clump != NOT_A_CLUMP && Mutex->Owner == Clump); //Always set new Owner to WaitQ's Head, since NOT_A_CLUMP means mutex is free Mutex->Owner = Mutex->WaitQ.Head; if (Mutex->Owner != NOT_A_CLUMP) { cCmdDeQClump(&(Mutex->WaitQ), Mutex->Owner); cCmdEnQClump(&(VarsCmd.RunQ), Mutex->Owner); } NXT_ASSERT(cCmdIsQSane(&(Mutex->WaitQ))); NXT_ASSERT(cCmdIsQSane(&(VarsCmd.RunQ))); return (NO_ERR); } // No instruction to do this yet, but put current clump to sleep until awakeTime occurs NXT_STATUS cCmdSleepClump(ULONG time) { CLUMP_ID Clump= VarsCmd.RunQ.Head; // save off before queue changes below CLUMP_REC * pClump = &(VarsCmd.pAllClumps[Clump]); cCmdDeQClump(&(VarsCmd.RunQ), Clump); cCmdEnQClump(&(VarsCmd.RestQ), Clump); pClump->awakenTime= time; return CLUMP_SUSPEND; } UBYTE cCmdCheckRestQ(ULONG currTime) { UBYTE awakened= FALSE; CLUMP_ID curr, next; CLUMP_REC * pClump; curr= VarsCmd.RestQ.Head; while(curr != NOT_A_CLUMP) { pClump= &(VarsCmd.pAllClumps[curr]); next= pClump->Link; if(pClump->awakenTime <= currTime) { pClump->awakenTime= 0; // not necessary, but for debugging identification cCmdDeQClump(&(VarsCmd.RestQ), curr); cCmdEnQClump(&(VarsCmd.RunQ), curr); awakened= TRUE; } curr= next; } return awakened; } NXT_STATUS cCmdSchedDependents(CLUMP_ID Clump, SWORD Begin, SWORD End) { CLUMP_ID CurrDepClumpID; SWORD i; //Begin and End specify range of CLUMP_IDs in dependent list to schedule //If either equals -1, both should equal -1, and no dependents will be scheduled //Else schedule specified subset offset from pDependents //Check for valid args NXT_ASSERT(cCmdIsClumpIDSane(Clump)); NXT_ASSERT((Begin >= 0 && End >= 0 && End < VarsCmd.pAllClumps[Clump].DependentCount) || (Begin == -1 && End == -1)); //If non-empty range if (Begin != -1 || End != -1) { //update dependents, scheduling if their CurrFireCount reaches 0 for (i = Begin; i <= End; i++) { CurrDepClumpID = VarsCmd.pAllClumps[Clump].pDependents[i]; NXT_ASSERT(cCmdIsClumpIDSane(CurrDepClumpID)); VarsCmd.pAllClumps[CurrDepClumpID].CurrFireCount--; if (VarsCmd.pAllClumps[CurrDepClumpID].CurrFireCount == 0) cCmdEnQClump(&(VarsCmd.RunQ), CurrDepClumpID); } } return (NO_ERR); } NXT_STATUS cCmdSchedDependent(CLUMP_ID Clump, CLUMP_ID TargetClump) { //TargetClump specifies the clump number of the target to schedule explicitly. //Check for valid args NXT_ASSERT(cCmdIsClumpIDSane(Clump)); NXT_ASSERT(cCmdIsClumpIDSane(TargetClump)); CLUMP_REC *clumpPtr= &VarsCmd.pAllClumps[TargetClump]; clumpPtr->CurrFireCount--; if (clumpPtr->CurrFireCount == 0) cCmdEnQClump(&(VarsCmd.RunQ), TargetClump); return (NO_ERR); } UBYTE cCmdIsClumpIDSane(CLUMP_ID Clump) { if (Clump < VarsCmd.AllClumpsCount) return TRUE; else return FALSE; } // // Memory pool management functions // void cCmdInitPool(void) { ULONG i; ULONG *poolPtr; //VarsCmd.Pool is a UBYTE pointer to ULONG array //This was done to enforce portable alignment. VarsCmd.Pool = (UBYTE*)(IOMapCmd.MemoryPool); for (i = (POOL_MAX_SIZE / 4), poolPtr= (ULONG*)&(POOL_START)[0]; i>0; i--, poolPtr++) *poolPtr = 0xDEADBEEF; VarsCmd.PoolSize = 0; } #if VMProfilingCode ULONG memMgrTime= 0; #endif NXT_STATUS cCmdDSArrayAlloc(DS_ELEMENT_ID DSElementID, UWORD Offset, UWORD NewCount) { NXT_STATUS Status = NO_ERR; UWORD DVIndex; UWORD OldCount; UWORD i; #if VMProfilingCode ULONG enterTime= dTimerReadHiRes(); #endif NXT_ASSERT(cCmdIsDSElementIDSane(DSElementID)); //Only arrays are valid here //!!! Recommended to upgrade NXT_ASSERT to ERR_INSTR return NXT_ASSERT(cCmdDSType(DSElementID) == TC_ARRAY); DVIndex = cCmdGetDVIndex(DSElementID, Offset); OldCount = DV_ARRAY[DVIndex].Count; if(OldCount == NewCount) goto allocExit; Status = cCmdDVArrayAlloc(DVIndex, NewCount); if (Status < NO_ERR) goto allocExit; if(!IS_AGGREGATE_TYPE(cCmdDSType(INC_ID(DSElementID)))) goto allocExit; if (OldCount > NewCount) { //Free dope vectors for sub-arrays. for (i = NewCount; i < OldCount; i++) { Status = cCmdFreeSubArrayDopeVectors(INC_ID(DSElementID), ARRAY_ELEM_OFFSET(DVIndex, i)); if (IS_ERR(Status)) goto allocExit; } } else if (OldCount < NewCount) { //Alloc dope vectors for sub-arrays. Set up DVIndexes for (i = OldCount; i < NewCount; i++) { Status = cCmdAllocSubArrayDopeVectors(INC_ID(DSElementID), ARRAY_ELEM_OFFSET(DVIndex, i)); if (IS_ERR(Status)) goto allocExit; } } NXT_ASSERT(cCmdVerifyMemMgr()); allocExit: #if VMProfilingCode memMgrTime += dTimerReadHiRes() - enterTime; #endif return Status; } NXT_STATUS cCmdDVArrayAlloc(DV_INDEX DVIndex, UWORD NewCount) { NXT_STATUS Status = NO_ERR; UBYTE *pData; UWORD ArraySize, InplaceSize; UWORD NextDVIndex; UWORD OldCount; OldCount = DV_ARRAY[DVIndex].Count; if (OldCount == NewCount) { //Nothing to alloc. Return. return Status; } else if (OldCount > NewCount) { //Already have the space. Shrink inplace. DV_ARRAY[DVIndex].Count = NewCount; return Status; } else // need to grow array { //Calculate new array size ArraySize = NewCount * DV_ARRAY[DVIndex].ElemSize; //Try growing inplace // If the Offset == NOT_AN_OFFSET then the array has never been allocated and can't grow inplace. if (DV_ARRAY[DVIndex].Offset != NOT_AN_OFFSET) { //Get pointer to next dope vector in dataspace if (DV_ARRAY[DVIndex].Link != NOT_A_DS_ID) { NextDVIndex = DV_ARRAY[DVIndex].Link; InplaceSize = DV_ARRAY[NextDVIndex].Offset - DV_ARRAY[DVIndex].Offset; } else { //Last element in dataspace. NXT_ASSERT(DVIndex == VarsCmd.MemMgr.Tail); InplaceSize = VarsCmd.DataspaceSize - DV_ARRAY[DVIndex].Offset; } if (ArraySize <= InplaceSize) { DV_ARRAY[DVIndex].Count = NewCount; return Status; } } //Can't grow inplace, have to allocate new space //Make sure we properly align for type //!!! This could also overflow memory (make PoolSize > POOL_MAX_SIZE) if we're within 3 bytes of the end. // I don't think it matters because if it does happend, we'll trigger the ERR_MEM below and compact. // During compaction, we'll reclaim these unused bytes. //!!! Aligning beginning of ALL arrays to 4 byte address ALIGN_TO_MOD(VarsCmd.PoolSize, SIZE_ULONG); ALIGN_TO_MOD(VarsCmd.DataspaceSize, SIZE_ULONG); if (VarsCmd.PoolSize + ArraySize >= POOL_MAX_SIZE) { //Not enough memory available return ERR_MEM; } //Get data from end of pool pData = VarsCmd.Pool + VarsCmd.PoolSize; //Grow pool and dataspace VarsCmd.PoolSize += ArraySize; VarsCmd.DataspaceSize += ArraySize; //Move old Array Data to new allocation if(OldCount) memmove(pData, VarsCmd.pDataspace + DV_ARRAY[DVIndex].Offset, (UWORD)(DV_ARRAY[DVIndex].ElemSize * OldCount)); //!!! Clear mem so old mem doesn't contain stale data. Not strictly needed. #if defined(WIN_DEBUG) || defined(ARM_DEBUG) memset(VarsCmd.pDataspace + DV_ARRAY[DVIndex].Offset, 0xFF, (UWORD)(DV_ARRAY[DVIndex].ElemSize * OldCount)); #endif //Update dope vector DV_ARRAY[DVIndex].Offset = pData - VarsCmd.pDataspace; DV_ARRAY[DVIndex].Count = NewCount; //Move dope vector to end of MemMgr list Status = cCmdMemMgrMoveToTail(DVIndex); if (IS_ERR(Status)) return Status; NXT_ASSERT(cCmdVerifyMemMgr()); } return Status; } //!!! Recursive function NXT_STATUS cCmdAllocSubArrayDopeVectors(DS_ELEMENT_ID DSElementID, UWORD Offset) { // Walks a single array element to see if it contains arrays // For any array it finds, a dope vector is allocated and the DVIndex is placed in the dataspace for the parent array. // This is a non-recursive function. It only walks the immediate array element. // DSElementID - ID of array sub-entry // Offset - offset to array element in dataspace NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode; DV_INDEX DVIndex; UWORD i; UWORD DVIndexOffset; //Offset to DVIndex field that points to the DopeVector from pDataspace UWORD LoopCount = 1; UWORD ElemSize; for (i = 0; i < LoopCount; i++) { TypeCode = cCmdDSType((DS_ELEMENT_ID)(DSElementID + i)); if (TypeCode == TC_CLUSTER) { LoopCount += cCmdClusterCount(DSElementID); } else if (TypeCode == TC_ARRAY) { //!!! ElemSize is a static value, but we don't have anywhere we put it (another TOC sub-entry?) // It'd be nice to not have to recalculate it. ElemSize = cCmdCalcArrayElemSize((DS_ELEMENT_ID)(DSElementID + i)); DVIndexOffset = VarsCmd.pDataspaceTOC[DSElementID + i].DSOffset + Offset; Status = cCmdAllocDopeVector(&DVIndex, ElemSize); if (IS_ERR(Status)) return Status; *((UWORD *)(VarsCmd.pDataspace + DVIndexOffset)) = DVIndex; } } return Status; } //!!! Recursive function NXT_STATUS cCmdFreeSubArrayDopeVectors(DS_ELEMENT_ID DSElementID, UWORD Offset) { // Walks a single array element to see if it contains arrays // Frees all dope vectors associated with the array element. // Recursively deletes sub-arrays. // DSElementID - ID of array sub-entry // Offset - offset to array element in dataspace NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode; DV_INDEX DVIndex; UWORD i, Count; TypeCode = cCmdDSType(DSElementID); if (TypeCode == TC_ARRAY) { DVIndex = cCmdGetDVIndex(DSElementID, Offset); NXT_ASSERT(DVIndex < DV_ARRAY[0].Count); Count = DV_ARRAY[DVIndex].Count; //Recur on sub-elements for (i = 0; i < Count; i++) { Status = cCmdFreeSubArrayDopeVectors(INC_ID(DSElementID), ARRAY_ELEM_OFFSET(DVIndex, i)); if (IS_ERR(Status)) return Status; } //Free Dope Vector Status = cCmdFreeDopeVector(DVIndex); } else if (TypeCode == TC_CLUSTER) { Count = cCmdClusterCount(DSElementID); DSElementID = INC_ID(DSElementID); //Recur on sub-elements for (i = 0; i < Count; i++) { Status = cCmdFreeSubArrayDopeVectors((DS_ELEMENT_ID)(DSElementID + i), Offset); if (IS_ERR(Status)) return Status; } } return Status; } NXT_STATUS cCmdAllocDopeVector(DV_INDEX *pIndex, UWORD ElemSize) { NXT_STATUS Status = NO_ERR; if (VarsCmd.MemMgr.FreeHead == NOT_A_DS_ID) { //No free DVs. Need to grow DopeVector array. Status = cCmdGrowDopeVectorArray(DV_ARRAY_GROWTH_COUNT); if (IS_ERR(Status)) return Status; } if(VarsCmd.MemMgr.FreeHead == NOT_A_DS_ID) return ERR_MEM; //Remove DV from free list *pIndex = VarsCmd.MemMgr.FreeHead; VarsCmd.MemMgr.FreeHead = DV_ARRAY[*pIndex].Link; if(VarsCmd.MemMgr.FreeHead != NOT_A_DS_ID) DV_ARRAY[VarsCmd.MemMgr.FreeHead].BackLink= NOT_A_DS_ID; //Add DV to tail of MemMgr list Status = cCmdMemMgrInsertAtTail(*pIndex); //Initialize values DV_ARRAY[*pIndex].Offset = NOT_AN_OFFSET; DV_ARRAY[*pIndex].ElemSize = ElemSize; DV_ARRAY[*pIndex].Count = 0; NXT_ASSERT(cCmdVerifyMemMgr()); return Status; } // //cCmdFreeDopeVector() - Open up a spot in the DopeVectorArray for future use // The DopeVectorArray doesn't shrink when arrays (and their dope vectors) are deleted. // Instead they're pushed on the free list and the array stays the same size. // Future allocations check the free list before resorting to cCmdGrowDopeVectorArray() // NXT_STATUS cCmdFreeDopeVector(DV_INDEX DVIndex) { NXT_STATUS Status = NO_ERR; DV_INDEX prev, post; //Bounds check NXT_ASSERT(DVIndex < DV_ARRAY[0].Count); //Reset dope vector fields DV_ARRAY[DVIndex].Count = 0; DV_ARRAY[DVIndex].ElemSize = 0; DV_ARRAY[DVIndex].Offset = NOT_AN_OFFSET; //Remove from MemMgr list if (DVIndex == VarsCmd.MemMgr.Head) { VarsCmd.MemMgr.Head = DV_ARRAY[DVIndex].Link; if(VarsCmd.MemMgr.Head != NOT_A_DS_ID) DV_ARRAY[VarsCmd.MemMgr.Head].BackLink= NOT_A_DS_ID; } else { // patchup middle or end of list. prev= DV_ARRAY[DVIndex].BackLink; post= DV_ARRAY[DVIndex].Link; NXT_ASSERT(prev != NOT_A_DS_ID); DV_ARRAY[prev].Link = post; if(post != NOT_A_DS_ID) DV_ARRAY[post].BackLink= prev; if (DVIndex == VarsCmd.MemMgr.Tail) VarsCmd.MemMgr.Tail = prev; //Make sure we found the previous DV, otherwise this DV was not in the the list (already freed?) } //Push onto free list DV_ARRAY[DVIndex].Link = VarsCmd.MemMgr.FreeHead; DV_ARRAY[DVIndex].BackLink = NOT_A_DS_ID; DV_ARRAY[VarsCmd.MemMgr.FreeHead].BackLink= DVIndex; VarsCmd.MemMgr.FreeHead = DVIndex; NXT_ASSERT(cCmdVerifyMemMgr()); return Status; } // //cCmdGrowDopeVectorArray() - expand DopeVectorArray to be able to track more dataspace arrays // NXT_STATUS cCmdGrowDopeVectorArray(UWORD NewNodesCount) { NXT_STATUS Status = NO_ERR; UWORD ArraySize; UWORD OldCount, NewCount, i; UBYTE * pData; NXT_ASSERT(cCmdVerifyMemMgr()); OldCount = DV_ARRAY[0].Count; NewCount = OldCount + NewNodesCount; NXT_ASSERT(NewCount > OldCount); ArraySize = DV_ARRAY[0].ElemSize * NewCount; //!!! Aligning beginning of ALL arrays to 4 byte address ALIGN_TO_MOD(VarsCmd.PoolSize, SIZE_ULONG); ALIGN_TO_MOD(VarsCmd.DataspaceSize, SIZE_ULONG); if (VarsCmd.PoolSize + ArraySize >= POOL_MAX_SIZE) { //Not enough memory available return ERR_MEM; } //Get data from end of pool pData = VarsCmd.Pool + VarsCmd.PoolSize; //Grow pool and dataspace VarsCmd.PoolSize += ArraySize; VarsCmd.DataspaceSize += ArraySize; //Move DopeVector Array memmove(pData, (UBYTE *)VarsCmd.MemMgr.pDopeVectorArray, (UWORD)(DV_ARRAY[0].ElemSize * DV_ARRAY[0].Count)); //Update MemMgr pointer VarsCmd.MemMgr.pDopeVectorArray = (DOPE_VECTOR *)pData; IOMapCmd.OffsetDVA = (UWORD)((ULONG)(VarsCmd.MemMgr.pDopeVectorArray) - (ULONG)&(IOMapCmd)); //Update dope vector DV_ARRAY[0].Offset = pData - VarsCmd.pDataspace; DV_ARRAY[0].Count = NewCount; //Add new DopeVectors to free list //Push in reverse order so they get popped in order (mostly for ease of debugging) for (i = NewCount - 1; i >= OldCount; i--) { DV_ARRAY[i].Offset = 0xFFFF; DV_ARRAY[i].ElemSize = 0; DV_ARRAY[i].Count = 0; DV_ARRAY[i].BackLink = NOT_A_DS_ID; if(VarsCmd.MemMgr.FreeHead != NOT_A_DS_ID) DV_ARRAY[VarsCmd.MemMgr.FreeHead].BackLink = i; DV_ARRAY[i].Link = VarsCmd.MemMgr.FreeHead; VarsCmd.MemMgr.FreeHead = i; } //Move dope vector to end of MemMgr list Status = cCmdMemMgrMoveToTail(0); NXT_ASSERT(cCmdVerifyMemMgr()); return Status; } UWORD cCmdCalcArrayElemSize(DS_ELEMENT_ID DSElementID) { TYPE_CODE TypeCode; UWORD SizeOfType; UWORD i; UWORD LoopCount = 1; UWORD Size = 0; UWORD Alignment = 0; NXT_ASSERT(cCmdDSType(DSElementID) == TC_ARRAY); DSElementID = INC_ID(DSElementID); for (i = 0; i < LoopCount; i++) { TypeCode = cCmdDSType((DS_ELEMENT_ID)(DSElementID + i)); if (TypeCode == TC_CLUSTER) { LoopCount += cCmdClusterCount((DS_ELEMENT_ID)(DSElementID + i)); } else { SizeOfType = cCmdSizeOf(TypeCode); ALIGN_TO_MOD(Size, SizeOfType); Size += SizeOfType; if (SizeOfType > Alignment) Alignment = SizeOfType; } } ALIGN_TO_MOD(Size, Alignment); return Size; } NXT_STATUS cCmdMemMgrMoveToTail(DV_INDEX DVIndex) { DV_INDEX prev, post; //Bounds check NXT_ASSERT(DVIndex < DV_ARRAY[0].Count); //Short circut if its already at the tail if (DVIndex == VarsCmd.MemMgr.Tail) return NO_ERR; if (DVIndex == VarsCmd.MemMgr.Head) { VarsCmd.MemMgr.Head = DV_ARRAY[DVIndex].Link; DV_ARRAY[VarsCmd.MemMgr.Head].BackLink= NOT_A_DS_ID; } else { // connect to middle or end of list. prev= DV_ARRAY[DVIndex].BackLink; post= DV_ARRAY[DVIndex].Link; NXT_ASSERT(prev != NOT_A_DS_ID); DV_ARRAY[prev].Link = post; if(post != NOT_A_DS_ID) DV_ARRAY[post].BackLink= prev; } DV_ARRAY[DVIndex].Link = NOT_A_DS_ID; DV_ARRAY[DVIndex].BackLink = VarsCmd.MemMgr.Tail; if(VarsCmd.MemMgr.Tail != NOT_A_DS_ID) DV_ARRAY[VarsCmd.MemMgr.Tail].Link = DVIndex; VarsCmd.MemMgr.Tail = DVIndex; NXT_ASSERT(cCmdVerifyMemMgr()); return NO_ERR; } NXT_STATUS cCmdMemMgrInsertAtTail(DV_INDEX DVIndex) { //Bounds check NXT_ASSERT(DVIndex < DV_ARRAY[0].Count); DV_ARRAY[VarsCmd.MemMgr.Tail].Link = DVIndex; DV_ARRAY[DVIndex].BackLink= VarsCmd.MemMgr.Tail; DV_ARRAY[DVIndex].Link = NOT_A_DS_ID; VarsCmd.MemMgr.Tail = DVIndex; NXT_ASSERT(cCmdVerifyMemMgr()); return NO_ERR; } UBYTE cCmdVerifyMemMgr() { DV_INDEX i, prev, post; UWORD CurrOffset = 0; UWORD PrevOffset = 0; UWORD DVCount = 0; //Make sure the MemMgr list is properly sorted in ascending offset order for (i = VarsCmd.MemMgr.Head; i != NOT_A_DS_ID; i = DV_ARRAY[i].Link) { CurrOffset = DV_ARRAY[i].Offset; if (CurrOffset != 0xFFFF) { if (PrevOffset > CurrOffset) return FALSE; PrevOffset = CurrOffset; } prev= DV_ARRAY[i].BackLink; post= DV_ARRAY[i].Link; if (post == NOT_A_DS_ID && i != VarsCmd.MemMgr.Tail) return FALSE; else if(prev == NOT_A_DS_ID && i != VarsCmd.MemMgr.Head) return FALSE; else if(prev != NOT_A_DS_ID && DV_ARRAY[prev].Link != i) return FALSE; else if(post != NOT_A_DS_ID && DV_ARRAY[post].BackLink != i) return FALSE; DVCount++; } // could check link and backlinks too for (i = VarsCmd.MemMgr.FreeHead; i != NOT_A_DS_ID; i = DV_ARRAY[i].Link) { DVCount++; } //Make sure the # of dope vectors = # used + # free if (DVCount != DV_ARRAY[0].Count) return FALSE; return TRUE; } NXT_STATUS cCmdDSCompact(void) { NXT_STATUS Status = NO_ERR; DV_INDEX CurrIndex; UWORD NewOffset; UWORD CurrOffset; UWORD Size; UWORD DeltaDSSize; UWORD TempOffset, TempSize; #if VM_BENCHMARK ULONG StartTime, TotalTime; VarsCmd.CompactionCount++; VarsCmd.LastCompactionTick = IOMapCmd.Tick - VarsCmd.StartTick; StartTime = dTimerRead(); #endif NXT_ASSERT(cCmdVerifyMemMgr()); NewOffset = VarsCmd.DSStaticSize; for (CurrIndex = VarsCmd.MemMgr.Head; CurrIndex != NOT_A_DS_ID; CurrIndex = DV_ARRAY[CurrIndex].Link) { //Align NewOffset for array to 4 byte address. ALIGN_TO_MOD(NewOffset, SIZE_ULONG); CurrOffset = DV_ARRAY[CurrIndex].Offset; if (CurrOffset != NOT_AN_OFFSET) { Size = DV_ARRAY[CurrIndex].ElemSize * DV_ARRAY[CurrIndex].Count; if (CurrOffset != NewOffset) { NXT_ASSERT(NewOffset < CurrOffset); memmove(VarsCmd.pDataspace + NewOffset, VarsCmd.pDataspace + CurrOffset, Size); // Clear mem to make stale data references more obvious while debugging. // Correct for overlapping memory regions (make sure we don't clear what we just moved). //!!! Clearing step not strictly necessary, so it could be optimized out if (NewOffset + Size > CurrOffset) { TempOffset = NewOffset + Size; TempSize = Size - (TempOffset - CurrOffset); } else { TempOffset = CurrOffset; TempSize = Size; } memset(VarsCmd.pDataspace + TempOffset, 0xFF, TempSize); //Update pDopeVectorArray if we move the dope vector array if (CurrIndex == 0) { VarsCmd.MemMgr.pDopeVectorArray = (DOPE_VECTOR *)(VarsCmd.pDataspace + NewOffset); IOMapCmd.OffsetDVA = (UWORD)((ULONG)(VarsCmd.MemMgr.pDopeVectorArray) - (ULONG)&(IOMapCmd)); } //Update offset in DV Array DV_ARRAY[CurrIndex].Offset = NewOffset; } NewOffset += Size; } } DeltaDSSize = VarsCmd.DataspaceSize - NewOffset; VarsCmd.PoolSize -= DeltaDSSize; VarsCmd.DataspaceSize -= DeltaDSSize; NXT_ASSERT(cCmdVerifyMemMgr()); #if VM_BENCHMARK TotalTime = dTimerRead() - StartTime; if (TotalTime > VarsCmd.MaxCompactionTime) VarsCmd.MaxCompactionTime = TotalTime; #endif return Status; } // // Message Queue functions // NXT_STATUS cCmdMessageWrite(UWORD QueueID, UBYTE * pData, UWORD Length) { NXT_STATUS Status = NO_ERR; if (pData == NULL) return ERR_ARG; if (QueueID >= MESSAGE_QUEUE_COUNT) return ERR_INVALID_QUEUE; if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) return ERR_NO_PROG; //Can't accept oversize messages because we treat them as strings (truncation would remove null termination) if (Length > MAX_MESSAGE_SIZE) return ERR_INVALID_SIZE; if (IS_DV_INDEX_SANE(GET_WRITE_MSG(QueueID))) { //A message is already there, the queue is full NXT_ASSERT(VarsCmd.MessageQueues[QueueID].WriteIndex == VarsCmd.MessageQueues[QueueID].ReadIndex); //Bump read index, drop existing message to make room for our new incoming message VarsCmd.MessageQueues[QueueID].ReadIndex = (VarsCmd.MessageQueues[QueueID].ReadIndex + 1) % MESSAGES_PER_QUEUE; } else { //Allocate dope vector for message Status = cCmdAllocDopeVector(&GET_WRITE_MSG(QueueID), 1); if (IS_ERR(Status)) return Status; } //Allocate storage for message Status = cCmdDVArrayAlloc(GET_WRITE_MSG(QueueID), Length); if (IS_ERR(Status)) { //Clear the dope vector for the message, since we're unable to put a message there. cCmdFreeDopeVector(GET_WRITE_MSG(QueueID)); SET_WRITE_MSG(QueueID, NOT_A_DS_ID); return Status; } //Copy message memmove(cCmdDVPtr(GET_WRITE_MSG(QueueID)), pData, Length); //Advance write index VarsCmd.MessageQueues[QueueID].WriteIndex = (VarsCmd.MessageQueues[QueueID].WriteIndex + 1) % MESSAGES_PER_QUEUE; return Status; } NXT_STATUS cCmdMessageGetSize(UWORD QueueID, UWORD * Size) { DV_INDEX ReadDVIndex; if (Size == NULL) return (ERR_ARG); if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) { *Size = 0; return (ERR_NO_PROG); } if (QueueID >= MESSAGE_QUEUE_COUNT) { *Size = 0; return (ERR_INVALID_QUEUE); } ReadDVIndex = GET_READ_MSG(QueueID); if (IS_DV_INDEX_SANE(ReadDVIndex)) { *Size = (DV_ARRAY[ReadDVIndex].Count); return (NO_ERR); } else { *Size = 0; return (STAT_MSG_EMPTY_MAILBOX); } } NXT_STATUS cCmdMessageRead(UWORD QueueID, UBYTE * pBuffer, UWORD Length, UBYTE Remove) { NXT_STATUS Status = NO_ERR; DV_INDEX ReadDVIndex; if (pBuffer == NULL) return (ERR_ARG); if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) return (ERR_NO_PROG); if (QueueID >= MESSAGE_QUEUE_COUNT) return (ERR_INVALID_QUEUE); ReadDVIndex = GET_READ_MSG(QueueID); if (IS_DV_INDEX_SANE(ReadDVIndex)) { //If Buffer doesn't have room for the entire message, //don't risk incomplete string floating around if (Length < DV_ARRAY[ReadDVIndex].Count) return (ERR_INVALID_SIZE); //Copy message memmove(pBuffer, cCmdDVPtr(ReadDVIndex), DV_ARRAY[ReadDVIndex].Count); if (Remove) { //Free memory used by message Status = cCmdFreeDopeVector(ReadDVIndex); if (IS_ERR(Status)) return Status; SET_READ_MSG(QueueID, NOT_A_DS_ID); //Advance read index VarsCmd.MessageQueues[QueueID].ReadIndex = (VarsCmd.MessageQueues[QueueID].ReadIndex + 1) % MESSAGES_PER_QUEUE; } } else { //No message to read, message Queue is empty NXT_ASSERT(VarsCmd.MessageQueues[QueueID].ReadIndex == VarsCmd.MessageQueues[QueueID].WriteIndex); return (STAT_MSG_EMPTY_MAILBOX); } return Status; } // // Datalog Queue function(s) // NXT_STATUS cCmdDatalogWrite(UBYTE * pData, UWORD Length) { NXT_STATUS Status = NO_ERR; if (pData == NULL) return ERR_ARG; if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) return (ERR_NO_PROG); //Can't accept oversize messages because we treat them as strings (truncation would remove null termination) if (Length > MAX_DATALOG_SIZE) return ERR_INVALID_SIZE; if (IS_DV_INDEX_SANE(GET_WRITE_DTLG())) { //A message is already there, the queue is full NXT_ASSERT(VarsCmd.DatalogBuffer.WriteIndex == VarsCmd.DatalogBuffer.ReadIndex); Status = STAT_MSG_BUFFERWRAP; //Bump read index, drop existing message to make room for our newly acquired datalog VarsCmd.DatalogBuffer.ReadIndex = (VarsCmd.DatalogBuffer.ReadIndex + 1) % DATALOG_QUEUE_DEPTH; } else { //Allocate dope vector for message Status = cCmdAllocDopeVector(&GET_WRITE_DTLG(), 1); if (IS_ERR(Status)) return Status; } //Allocate storage for message Status |= cCmdDVArrayAlloc(GET_WRITE_DTLG(), Length); if (IS_ERR(Status)) { //Clear the dope vector for the message, since we're unable to put a message there. cCmdFreeDopeVector(GET_WRITE_DTLG()); SET_WRITE_DTLG(NOT_A_DS_ID); return Status; } //Copy message memmove(cCmdDVPtr(GET_WRITE_DTLG()), pData, Length); //Advance write index VarsCmd.DatalogBuffer.WriteIndex = (VarsCmd.DatalogBuffer.WriteIndex + 1) % DATALOG_QUEUE_DEPTH; return Status; } NXT_STATUS cCmdDatalogGetSize(UWORD * Size) { DV_INDEX ReadDVIndex; if (Size == NULL) return (ERR_ARG); if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) { *Size = 0; return (ERR_NO_PROG); } ReadDVIndex = GET_READ_DTLG(); if (IS_DV_INDEX_SANE(ReadDVIndex)) { *Size = (DV_ARRAY[ReadDVIndex].Count); return (NO_ERR); } else { *Size = 0; return (STAT_MSG_EMPTY_MAILBOX); } } NXT_STATUS cCmdDatalogRead(UBYTE * pBuffer, UWORD Length, UBYTE Remove) { NXT_STATUS Status = NO_ERR; DV_INDEX ReadDVIndex; if (pBuffer == NULL) return (ERR_ARG); if (VarsCmd.ActiveProgHandle == NOT_A_HANDLE) return (ERR_NO_PROG); ReadDVIndex = GET_READ_DTLG(); if (IS_DV_INDEX_SANE(ReadDVIndex)) { //If Buffer doesn't have room for the entire message, //don't risk incomplete string floating around if (Length < DV_ARRAY[ReadDVIndex].Count) return (ERR_INVALID_SIZE); //Copy message memmove(pBuffer, cCmdDVPtr(ReadDVIndex), DV_ARRAY[ReadDVIndex].Count); if (Remove) { //Free memory used by message Status = cCmdFreeDopeVector(ReadDVIndex); if (IS_ERR(Status)) return Status; SET_READ_DTLG(NOT_A_DS_ID); //Advance read index VarsCmd.DatalogBuffer.ReadIndex = (VarsCmd.DatalogBuffer.ReadIndex + 1) % DATALOG_QUEUE_DEPTH; } } else { //No message to read, datalog Queue is empty NXT_ASSERT(VarsCmd.DatalogBuffer.ReadIndex == VarsCmd.DatalogBuffer.WriteIndex); return (STAT_MSG_EMPTY_MAILBOX); } return Status; } // // Color Sensor Functions // NXT_STATUS cCmdColorSensorRead (UBYTE Port, SWORD * SensorValue, UWORD * RawArray, UWORD * NormalizedArray, SWORD * ScaledArray, UBYTE * InvalidData) { ULONG i; //Make sure Port is valid for Color Sensor INPUTSTRUCT * pIn = &(pMapInput->Inputs[Port]); UBYTE sType = pIn->SensorType; if (!(sType == COLORFULL || sType == COLORRED || sType == COLORGREEN || sType == COLORBLUE || sType == COLORNONE)) { return (ERR_COMM_CHAN_NOT_READY); //TODO - is this the right error? } //Copy Detected Color *SensorValue = pIn->SensorValue; //Copy all raw, normalized and scaled data from I/O Map for (i=0; iColors[Port]); RawArray[i] = pColor->ADRaw[i]; NormalizedArray[i] = pColor->SensorRaw[i]; ScaledArray[i] = pColor->SensorValue[i]; } //Copy the Invalid Data Flag *InvalidData = pIn->InvalidData; return NO_ERR; } // // Dataspace Support functions // UBYTE cCmdIsDSElementIDSane(DS_ELEMENT_ID Index) { if (Index < VarsCmd.DataspaceCount) return TRUE; else return FALSE; } void * cCmdResolveDataArg(DATA_ARG DataArg, UWORD Offset, TYPE_CODE * TypeCode) { void * ret_val = NULL; //!!! DATA_ARG masking system only for internal c_cmd use! // All normal bytecode arguments should go through top if() block. NXT_ASSERT(cCmdIsDSElementIDSane(DataArg)); ret_val = cCmdDSPtr(DataArg, Offset); if (TypeCode) *TypeCode = VarsCmd.pDataspaceTOC[DataArg].TypeCode; //!!! Caller beware! If DataArg isn't sane, ret_val may be out of range or NULL! return ret_val; } // normal Resolve handles both, but this is specific to I/O args void * cCmdResolveIODataArg(DATA_ARG DataArg, ULONG Offset, TYPE_CODE * TypeCode) { void * ret_val = NULL; ULONG ModuleID; ULONG FieldID; //DataArg refers to a field in the IO map // ModuleID = ((DataArg >> 9) & 0x1F); ModuleID = ((DataArg & 0x3FFF) >> 9); FieldID = (DataArg & 0x01FF); //!!! Preliminary bounds check -- still could allow invalid combos through if (ModuleID > MOD_OUTPUT || FieldID >= IO_OUT_FIELD_COUNT) { NXT_BREAK; return NULL; } ret_val = IO_PTRS[ModuleID][FieldID]; if (TypeCode) *TypeCode = IO_TYPES[ModuleID][FieldID]; return ret_val; } void cCmdSetValFlt(void * pVal, TYPE_CODE TypeCode, float NewVal) { /* * According to C standard, converting a float to integer is implementation * defined when the number is out of integer bounds. * * To match original LEGO firmware, when casting float as integer, always * convert to signed value, then cast as destination type. */ if (pVal) { if (TypeCode == TC_FLOAT) *(float*)pVal = NewVal; else { SLONG i = NewVal; switch (TypeCode) { case TC_ULONG: case TC_SLONG: { *(ULONG*)pVal = i; } break; case TC_UWORD: case TC_SWORD: { *(UWORD*)pVal = i; } break; case TC_UBYTE: case TC_SBYTE: { *(UBYTE*)pVal = i; } break; } } } return; } ULONG cCmdGetUByte(void * pVal); ULONG cCmdGetSByte(void * pVal); ULONG cCmdGetUWord(void * pVal); ULONG cCmdGetSWord(void * pVal); ULONG cCmdGetULong(void * pVal); ULONG cCmdGetSLong(void * pVal); ULONG cCmdGetError(void * pVal); ULONG cCmdGetFloat(void * pVal); void cCmdSetByte(void * pVal, ULONG NewVal); void cCmdSetWord(void * pVal, ULONG NewVal); void cCmdSetLong(void * pVal, ULONG NewVal); void cCmdSetError(void * pVal, ULONG NewVal); typedef ULONG (*pGetOperand)(void *); static pGetOperand GetProcArray[11]= {cCmdGetUByte, cCmdGetUByte, cCmdGetSByte, cCmdGetUWord, cCmdGetSWord, cCmdGetULong, cCmdGetSLong, cCmdGetError, cCmdGetError, cCmdGetError, cCmdGetFloat}; // dup UByte to line up typedef void (*pSetOperand)(void *, ULONG); static pSetOperand SetProcArray[9]= {cCmdSetByte, cCmdSetByte, cCmdSetByte, cCmdSetWord, cCmdSetWord, cCmdSetLong, cCmdSetLong, cCmdSetError, cCmdSetError}; // dup UByte to line up void cCmdSetError(void * pVal, ULONG NewVal) { NXT_BREAK; } void cCmdSetLong(void * pVal, ULONG NewVal) { *(ULONG*)pVal = NewVal; } void cCmdSetWord(void * pVal, ULONG NewVal) { *(UWORD*)pVal = (UWORD)NewVal; } void cCmdSetByte(void * pVal, ULONG NewVal) { *(UBYTE*)pVal = (UBYTE)NewVal; } // only works on simple types, equivalent to resolve and get, but faster ULONG cCmdGetScalarValFromDataArg(DATA_ARG DataArg, UWORD Offset) { DS_TOC_ENTRY *dsTOCPtr= &VarsCmd.pDataspaceTOC[DataArg]; return GetProcArray[dsTOCPtr->TypeCode](VarsCmd.pDataspace + dsTOCPtr->DSOffset + Offset); } ULONG cCmdGetError(void * pVal) { NXT_BREAK; return 0; } ULONG cCmdGetULong(void * pVal) { return (ULONG)(*(ULONG*)pVal); } ULONG cCmdGetSLong(void * pVal) { return (SLONG)(*(SLONG*)pVal); } ULONG cCmdGetUWord(void * pVal) { return (UWORD)(*(UWORD*)pVal); } ULONG cCmdGetSWord(void * pVal) { return (SWORD)(*(SWORD*)pVal); } ULONG cCmdGetUByte(void * pVal) { return (UBYTE)(*(UBYTE*)pVal); } ULONG cCmdGetSByte(void * pVal) { return (SBYTE)(*(SBYTE*)pVal); } ULONG cCmdGetFloat(void * pVal) { float tempVal = *(float*)pVal; if (tempVal >= 0.0f) { tempVal += 0.5f; } else { tempVal -= 0.5f; } /* * According to C standard, converting a float to integer is implementation * defined when the number is out of integer bounds. * * To match original LEGO firmware, when casting float as integer, always * convert to signed value, then cast as destination type. */ return (ULONG)(SLONG)tempVal; } ULONG cCmdGetVal(void * pVal, TYPE_CODE TypeCode) { if (pVal) return GetProcArray[TypeCode](pVal); else //!!! Default return value places responsibility on caller to use this function wisely return 0; } float cCmdGetValFlt(void * pVal, TYPE_CODE TypeCode) { if (pVal) { switch (TypeCode) { case TC_ULONG: { return (ULONG)(*(ULONG*)pVal); } case TC_SLONG: { return (SLONG)(*(SLONG*)pVal); } case TC_UWORD: { return (UWORD)(*(UWORD*)pVal); } case TC_SWORD: { return (SWORD)(*(SWORD*)pVal); } case TC_UBYTE: { return (UBYTE)(*(UBYTE*)pVal); } case TC_SBYTE: { return (SBYTE)(*(SBYTE*)pVal); } case TC_FLOAT: { return (float)(*(float*)pVal); } default: break; } } //!!! Default return value places responsibility on caller to use this function wisely return 0; } // Only for scalar types and no offset void cCmdSetScalarValFromDataArg(DATA_ARG DataArg, ULONG NewVal) { DS_TOC_ENTRY *dsTOCPtr= &VarsCmd.pDataspaceTOC[DataArg]; SetProcArray[dsTOCPtr->TypeCode](VarsCmd.pDataspace + dsTOCPtr->DSOffset, NewVal); } void cCmdSetVal(void * pVal, TYPE_CODE TypeCode, ULONG NewVal) { if (pVal) SetProcArray[TypeCode](pVal, NewVal); } void* cCmdDSPtr(DS_ELEMENT_ID DSElementID, UWORD Offset) { void * pDSItem; DV_INDEX DVIndex; TYPE_CODE TypeCode; NXT_ASSERT(cCmdIsDSElementIDSane(DSElementID)); TypeCode = cCmdDSType(DSElementID); if (TypeCode == TC_ARRAY) { //!!! Empty arrays return NULL. if (cCmdArrayCount(DSElementID, Offset) == 0) pDSItem = NULL; else { DVIndex = cCmdGetDVIndex(DSElementID, Offset); pDSItem = (VarsCmd.pDataspace + DV_ARRAY[DVIndex].Offset); } } else if (TypeCode == TC_CLUSTER) { NXT_ASSERT(cCmdClusterCount(DSElementID) != 0) //Returning pointer to the first element in the cluster pDSItem = cCmdDSPtr(INC_ID(DSElementID), Offset); } else pDSItem = (VarsCmd.pDataspace + VarsCmd.pDataspaceTOC[DSElementID].DSOffset + Offset); NXT_ASSERT((UBYTE*)pDSItem < POOL_SENTINEL); return pDSItem; } void* cCmdDVPtr(DV_INDEX DVIndex) { NXT_ASSERT(IS_DV_INDEX_SANE(DVIndex)); return (VarsCmd.pDataspace + DV_ARRAY[DVIndex].Offset); } //!!! Recursive function DS_ELEMENT_ID cCmdNextDSElement(DS_ELEMENT_ID CurrID) { DS_ELEMENT_ID NextID; TYPE_CODE CurrType; UWORD ClusterCount, i; NXT_ASSERT(cCmdIsDSElementIDSane(CurrID)); NextID = CurrID + 1; if (!cCmdIsDSElementIDSane(NextID)) return NOT_A_DS_ID; CurrType = cCmdDSType(CurrID); if (CurrType == TC_ARRAY) { //Arrays contain two elements. Advance past the second one. NextID = cCmdNextDSElement(NextID); } else if (CurrType == TC_CLUSTER) { ClusterCount = cCmdClusterCount(CurrID); for (i = 0; i < ClusterCount; i++) { NextID = cCmdNextDSElement(NextID); } } return NextID; } //!!! Recursive function UBYTE cCmdCompareDSType(DS_ELEMENT_ID DSElementID1, DS_ELEMENT_ID DSElementID2) { TYPE_CODE Type1, Type2; UWORD i, Count1, Count2; Type1 = cCmdDSType(DSElementID1); Type2 = cCmdDSType(DSElementID2); if (Type1 != Type2) return FALSE; if (Type1 == TC_CLUSTER) { Count1 = cCmdClusterCount(DSElementID1); Count2 = cCmdClusterCount(DSElementID2); if(Count1 != Count2) return FALSE; DSElementID1 = INC_ID(DSElementID1); DSElementID2 = INC_ID(DSElementID2); for (i = 0; i < Count1; i++) { if (!cCmdCompareDSType(DSElementID1, DSElementID2)) return FALSE; DSElementID1 = cCmdNextDSElement(DSElementID1); DSElementID2 = cCmdNextDSElement(DSElementID2); } } else if (Type1 == TC_ARRAY) { if (!cCmdCompareDSType(INC_ID(DSElementID1), INC_ID(DSElementID2))) return FALSE; } return TRUE; } //!!! Recursive function UWORD cCmdCalcFlattenedSize(DS_ELEMENT_ID DSElementID, UWORD Offset) { UWORD Size = 0; TYPE_CODE TypeCode; DV_INDEX DVIndex; UWORD i; UWORD Count; TypeCode = cCmdDSType(DSElementID); if (TypeCode == TC_ARRAY) { DVIndex = cCmdGetDVIndex(DSElementID, Offset); DSElementID = INC_ID(DSElementID); TypeCode = cCmdDSType(DSElementID); if (!IS_AGGREGATE_TYPE(TypeCode)) { //Short circuit recursive calculation if our array sub-type is a scalar Size += DV_ARRAY[DVIndex].ElemSize * DV_ARRAY[DVIndex].Count; } else { //If the sub type is an aggregate type, then it can contain arrays, so we have to recur for (i = 0; i < DV_ARRAY[DVIndex].Count; i++) { Size += cCmdCalcFlattenedSize(DSElementID, ARRAY_ELEM_OFFSET(DVIndex, i)); } } } else if (TypeCode == TC_CLUSTER) { Count = cCmdClusterCount(DSElementID); DSElementID = INC_ID(DSElementID); for (i = 0; i < Count; i++) { Size += cCmdCalcFlattenedSize(DSElementID, Offset); DSElementID = cCmdNextDSElement(DSElementID); } } else //Scalar { Size += cCmdSizeOf(TypeCode); } return Size; } //!!! Recursive function NXT_STATUS cCmdFlattenToByteArray(UBYTE * pByteArray, UWORD * pByteOffset, DS_ELEMENT_ID DSElementID, UWORD Offset) { NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode; DV_INDEX DVIndex; UWORD i; UWORD Count; UBYTE *pVal; TypeCode = cCmdDSType(DSElementID); if (TypeCode == TC_ARRAY) { DVIndex = cCmdGetDVIndex(DSElementID, Offset); Count = DV_ARRAY[DVIndex].Count; DSElementID = INC_ID(DSElementID); TypeCode = cCmdDSType(DSElementID); if (!IS_AGGREGATE_TYPE(TypeCode)) { //Short circuit recursive calculation if our array sub-type is a scalar Count = DV_ARRAY[DVIndex].ElemSize * DV_ARRAY[DVIndex].Count; memmove((pByteArray + *pByteOffset), (VarsCmd.pDataspace + DV_ARRAY[DVIndex].Offset), Count); *pByteOffset += Count; } else { //If the sub type is an aggregate type, then it can contain arrays, so we have to recur for (i = 0; i < Count; i++) { cCmdFlattenToByteArray(pByteArray, pByteOffset, DSElementID, ARRAY_ELEM_OFFSET(DVIndex, i)); } } } else if (TypeCode == TC_CLUSTER) { Count = cCmdClusterCount(DSElementID); DSElementID = INC_ID(DSElementID); for (i = 0; i < Count; i++) { cCmdFlattenToByteArray(pByteArray, pByteOffset, DSElementID, Offset); DSElementID = cCmdNextDSElement(DSElementID); } } else //Scalar { pVal = cCmdResolveDataArg(DSElementID, Offset, NULL); Count = cCmdSizeOf(TypeCode); memmove((pByteArray + *pByteOffset), pVal, Count); *pByteOffset += Count; } return Status; } NXT_STATUS cCmdUnflattenFromByteArray(UBYTE * pByteArray, UWORD * pByteOffset, DS_ELEMENT_ID DSElementID, UWORD Offset) { NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode; DV_INDEX DVIndex; UWORD i; UWORD Count; UBYTE *pVal; TypeCode = cCmdDSType(DSElementID); if (TypeCode == TC_ARRAY) { DVIndex = cCmdGetDVIndex(DSElementID, Offset); Count = DV_ARRAY[DVIndex].Count; DSElementID = INC_ID(DSElementID); TypeCode = cCmdDSType(DSElementID); if (!IS_AGGREGATE_TYPE(TypeCode)) { //Short circuit recursive calculation if our array sub-type is a scalar Count = DV_ARRAY[DVIndex].ElemSize * DV_ARRAY[DVIndex].Count; memmove((VarsCmd.pDataspace + DV_ARRAY[DVIndex].Offset), (pByteArray + *pByteOffset), Count); *pByteOffset += Count; } else { //If the sub type is an aggregate type, then it can contain arrays, so we have to recur for (i = 0; i < Count; i++) { cCmdUnflattenFromByteArray(pByteArray, pByteOffset, DSElementID, ARRAY_ELEM_OFFSET(DVIndex, i)); } } } else if (TypeCode == TC_CLUSTER) { Count = cCmdClusterCount(DSElementID); DSElementID = INC_ID(DSElementID); for (i = 0; i < Count; i++) { cCmdUnflattenFromByteArray(pByteArray, pByteOffset, DSElementID, Offset); DSElementID = cCmdNextDSElement(DSElementID); } } else //Scalar { pVal = cCmdResolveDataArg(DSElementID, Offset, NULL); Count = cCmdSizeOf(TypeCode); memmove(pVal, (pByteArray + *pByteOffset), Count); *pByteOffset += Count; } return Status; } UWORD cCmdClusterCount(DS_ELEMENT_ID DSElementID) { UWORD ClusterCount; NXT_ASSERT(cCmdIsDSElementIDSane(DSElementID)); NXT_ASSERT(cCmdDSType(DSElementID) == TC_CLUSTER); ClusterCount = VarsCmd.pDataspaceTOC[DSElementID].DSOffset; return ClusterCount; } UWORD cCmdGetDVIndex(DS_ELEMENT_ID DSElementID, UWORD Offset) { UWORD DVIndex; NXT_ASSERT(cCmdDSType(DSElementID) == TC_ARRAY); DVIndex = *(UWORD *)(VarsCmd.pDataspace + VarsCmd.pDataspaceTOC[DSElementID].DSOffset + Offset); //Make sure we're returning a valid DVIndex NXT_ASSERT(DVIndex != 0 && DVIndex < DV_ARRAY[0].Count); return DVIndex; } UWORD cCmdArrayCount(DS_ELEMENT_ID DSElementID, UWORD Offset) { DV_INDEX DVIndex; NXT_ASSERT(cCmdIsDSElementIDSane(DSElementID)); NXT_ASSERT(cCmdDSType(DSElementID) == TC_ARRAY); DVIndex = cCmdGetDVIndex(DSElementID, Offset); return DV_ARRAY[DVIndex].Count; } TYPE_CODE cCmdArrayType(DS_ELEMENT_ID DSElementID) { TYPE_CODE TypeCode; NXT_ASSERT(cCmdIsDSElementIDSane(DSElementID)); NXT_ASSERT(cCmdIsDSElementIDSane(INC_ID(DSElementID))); NXT_ASSERT(cCmdDSType(DSElementID) == TC_ARRAY); TypeCode = VarsCmd.pDataspaceTOC[DSElementID + 1].TypeCode; return TypeCode; } DS_ELEMENT_ID cCmdGetDataspaceCount(void) { return (VarsCmd.DataspaceCount); } UBYTE cCmdCompare(UBYTE CompCode, ULONG Val1, ULONG Val2, TYPE_CODE TypeCode1, TYPE_CODE TypeCode2) { SLONG SVal1, SVal2; if (QUICK_UNSIGNED_TEST(TypeCode1) || QUICK_UNSIGNED_TEST(TypeCode2)) { return ((CompCode == OPCC1_LT && Val1 < Val2) || (CompCode == OPCC1_GT && Val1 > Val2) || (CompCode == OPCC1_LTEQ && Val1 <= Val2) || (CompCode == OPCC1_GTEQ && Val1 >= Val2) || (CompCode == OPCC1_EQ && Val1 == Val2) || (CompCode == OPCC1_NEQ && Val1 != Val2)); } else { SVal1 = (SLONG)Val1; SVal2 = (SLONG)Val2; return ((CompCode == OPCC1_LT && SVal1 < SVal2) || (CompCode == OPCC1_GT && SVal1 > SVal2) || (CompCode == OPCC1_LTEQ && SVal1 <= SVal2) || (CompCode == OPCC1_GTEQ && SVal1 >= SVal2) || (CompCode == OPCC1_EQ && SVal1 == SVal2) || (CompCode == OPCC1_NEQ && SVal1 != SVal2)); } } UBYTE cCmdCompareFlt(UBYTE CompCode, float Val1, float Val2, TYPE_CODE TypeCode1, TYPE_CODE TypeCode2) { //!!! add threshold to equality comparisons return ((CompCode == OPCC1_LT && Val1 < Val2) || (CompCode == OPCC1_GT && Val1 > Val2) || (CompCode == OPCC1_LTEQ && Val1 <= Val2) || (CompCode == OPCC1_GTEQ && Val1 >= Val2) || (CompCode == OPCC1_EQ && Val1 == Val2) || (CompCode == OPCC1_NEQ && Val1 != Val2)); } NXT_STATUS cCmdCompareAggregates(UBYTE CompCode, UBYTE *ReturnBool, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3) { NXT_STATUS Status = NO_ERR; UBYTE Finished; Finished = FALSE; Status = cCmdRecursiveCompareAggregates(CompCode, ReturnBool, &Finished, Arg2, Offset2, Arg3, Offset3); if (Finished == FALSE) { //If Finished has not been set to TRUE, it means that it was unable to find an inequality, thereby ending the comparison. //Both elements are equal. Assign the proper value to ReturnBool *ReturnBool = (CompCode == OPCC1_EQ || CompCode == OPCC1_GTEQ || CompCode == OPCC1_LTEQ); } return Status; } //!!! Recursive function NXT_STATUS cCmdRecursiveCompareAggregates(UBYTE CompCode, UBYTE *ReturnBool, UBYTE *Finished, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3) { //The value of Finished must be set to FALSE before calling this function. //We are able to determine the result of the comparison once we find an inequality. //Once an inequality is found, Finished is set to TRUE and ReturnBool is set based on the CompCode. //A call to this function will return with Finished still equal to FALSE if both elements are equal in value and count. //It is the caller of this function's job to set ReturnBool if this function returns with Finished == FALSE. NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode2, TypeCode3; DV_INDEX DVIndex2, DVIndex3; ULONG ArgVal2, ArgVal3; UWORD Count2, Count3, MinCount; UWORD i; TypeCode2 = cCmdDSType(Arg2); TypeCode3 = cCmdDSType(Arg3); //Make sure the two things we're comparing are the same type if (IS_AGGREGATE_TYPE(TypeCode2) && (TypeCode2 != TypeCode3)) { NXT_BREAK; return ERR_ARG; } //Simple case, both args are scalars. Solve and return. if (!IS_AGGREGATE_TYPE(TypeCode2)) { ArgVal2 = cCmdGetScalarValFromDataArg(Arg2, Offset2); ArgVal3 = cCmdGetScalarValFromDataArg(Arg3, Offset3); //Once we find an inequality, we can determine the result of the comparison *Finished = cCmdCompare(OPCC1_NEQ, ArgVal2, ArgVal3, TypeCode2, TypeCode3); if (*Finished) *ReturnBool = cCmdCompare(CompCode, ArgVal2, ArgVal3, TypeCode2, TypeCode3); return Status; } // Initialize local variables for each argument if (TypeCode2 == TC_ARRAY) { Count2 = cCmdArrayCount(Arg2, Offset2); DVIndex2 = cCmdGetDVIndex(Arg2, Offset2); Offset2 = DV_ARRAY[DVIndex2].Offset; Count3 = cCmdArrayCount(Arg3, Offset3); DVIndex3 = cCmdGetDVIndex(Arg3, Offset3); Offset3 = DV_ARRAY[DVIndex3].Offset; } else if (TypeCode2 == TC_CLUSTER) { Count2 = cCmdClusterCount(Arg2); Count3 = cCmdClusterCount(Arg3); } //Short circuit evaluation of EQ and NEQ if counts are different if (Count2 != Count3) { if ((CompCode == OPCC1_EQ) || (CompCode == OPCC1_NEQ)) { *Finished = TRUE; *ReturnBool = (CompCode == OPCC1_NEQ); return Status; } } MinCount = (Count2 < Count3) ? Count2 : Count3; //Advance aggregate args to first sub-element for next call Arg2 = INC_ID(Arg2); Arg3 = INC_ID(Arg3); // // Loop through the sub-elements of aggregate arguments. // Call cCmdRecursiveCompareAggregates recursively with simpler type. // for (i = 0; i < MinCount; i++) { Status = cCmdRecursiveCompareAggregates(CompCode, ReturnBool, Finished, Arg2, Offset2, Arg3, Offset3); if (*Finished || IS_ERR(Status)) return Status; //Advance aggregate args to next sub-element if (TypeCode2 == TC_ARRAY) { Offset2 += DV_ARRAY[DVIndex2].ElemSize; Offset3 += DV_ARRAY[DVIndex3].ElemSize; } else if (TypeCode2 == TC_CLUSTER) { Arg2 = cCmdNextDSElement(Arg2); Arg3 = cCmdNextDSElement(Arg3); } } //All elements in aggregates type up to MinCount are equal. Count discrepancy determines comparison outcome. if (Count2 != Count3) { *Finished = TRUE; *ReturnBool = cCmdCompare(CompCode, Count2, Count3, TC_UWORD, TC_UWORD); } //Else, no size discrepancy. Elements are equal. Comparison still not resolved, //so return !Finished and status back up the call chain for further comparison return Status; } ULONG gClearProfileInfo= 0, bigExecTime= 0; #if VMProfilingCode void UpdateProfileInfo(ULONG shortOp, CODE_WORD *pInstr, ULONG execTime, ULONG InstrSize) { ULONG j; ULONG opCode; if(execTime > 500 && shortOp == 8) bigExecTime= shortOp; if(gClearProfileInfo) { ExecutedInstrs= 0; CmdCtrlTime= 0; OverheadTime= 0; CmdCtrlCalls= 0; LastAvgCount= 0; for(j= 0; j < 255; j++) CmdCtrlClumpTime[j]= 0; for(j= 0; j < OPCODE_COUNT; j++) { InstrProfile[j].Avg= 0; InstrProfile[j].Time= 0; InstrProfile[j].Count= 0; InstrProfile[j].Max= 0; } for(j= 0; j < SYSCALL_COUNT; j++) { SysCallProfile[j].Avg= 0; SysCallProfile[j].Time= 0; SysCallProfile[j].Count= 0; SysCallProfile[j].Max= 0; } for(j= 0; j < NUM_SHORT_OPCODE_COUNT; j++) { ShortInstrProfile[j].Avg= 0; ShortInstrProfile[j].Time= 0; ShortInstrProfile[j].Count= 0; ShortInstrProfile[j].Max= 0; } for(j= 0; j < NUM_INTERP_FUNCS; j++) { InterpFuncProfile[j].Avg= 0; InterpFuncProfile[j].Time= 0; InterpFuncProfile[j].Count= 0; InterpFuncProfile[j].Max= 0; } gClearProfileInfo= FALSE; } ExecutedInstrs ++; if(shortOp > 7) // shortop bit set { ShortInstrProfile[shortOp-8].Time += execTime; ShortInstrProfile[shortOp-8].Count++; if(execTime > ShortInstrProfile[shortOp-8].Max) ShortInstrProfile[shortOp-8].Max= execTime; } else { opCode = OP_CODE(pInstr); InstrProfile[opCode].Time += execTime; InstrProfile[opCode].Count++; if(execTime > InstrProfile[opCode].Max) InstrProfile[opCode].Max= execTime; if(opCode == OP_SYSCALL) { SysCallProfile[GetDataArg(pInstr[1])].Time += execTime; SysCallProfile[GetDataArg(pInstr[1])].Count++; if(execTime > SysCallProfile[GetDataArg(pInstr[1])].Max) SysCallProfile[GetDataArg(pInstr[1])].Max= execTime; } InterpFuncProfile[InstrSize].Time += execTime; InterpFuncProfile[InstrSize].Count++; if(execTime > InterpFuncProfile[InstrSize].Max) InterpFuncProfile[InstrSize].Max= execTime; } if(ExecutedInstrs - LastAvgCount > 999) // every N instrs, update avgs { for(j= 0; j < OPCODE_COUNT; j++) if(InstrProfile[j].Count) InstrProfile[j].Avg= InstrProfile[j].Time/InstrProfile[j].Count; for(j= 0; j < SYSCALL_COUNT; j++) if(SysCallProfile[j].Count) SysCallProfile[j].Avg= SysCallProfile[j].Time/SysCallProfile[j].Count; for(j= 0; j < NUM_SHORT_OPCODE_COUNT; j++) if(ShortInstrProfile[j].Count) ShortInstrProfile[j].Avg= ShortInstrProfile[j].Time/ShortInstrProfile[j].Count; for(j= 0; j < NUM_INTERP_FUNCS; j++) if(InterpFuncProfile[j].Count) InterpFuncProfile[j].Avg= InterpFuncProfile[j].Time/InterpFuncProfile[j].Count; LastAvgCount= ExecutedInstrs; } } #endif // // Interpreter Functions // NXT_STATUS cCmdInterpFromClump() { CLUMP_ID Clump= VarsCmd.RunQ.Head; NXT_STATUS Status = NO_ERR; CLUMP_REC * pClumpRec; CODE_WORD * pInstr, *lastClumpInstr; UBYTE InstrSize; ULONG shortOp, nextMSTick; SLONG i; #if VM_BENCHMARK ULONG InstrTime = dTimerRead(); #endif if (!cCmdIsClumpIDSane(Clump)) // this means all clumps are asleep return TIMES_UP; //Resolve clump record structure and current instruction pointer pClumpRec = &(VarsCmd.pAllClumps[Clump]); pInstr = pClumpRec->PC; // abs lastClumpInstr= pClumpRec->CodeEnd; // abs i= gInstrsToExecute; nextMSTick= dTimerGetNextMSTickCnt(); do { #if VMProfilingCode ULONG instrStartTime; instrStartTime= dTimerReadHiRes(); #endif ULONG instrWord= *(UWORD*)pInstr; shortOp= (instrWord>>8) & 0x0F; if(shortOp > 7) // shortop bit set Status= ShortInterpFuncs[shortOp - 8](pInstr); else { // we know this is a long instr, dispatch on num params, which correlates to size InstrSize = INSTR_SIZE(instrWord); // keep in a local for profiling Status = (*InterpFuncs[InstrSize])(pInstr); } #if VMProfilingCode UpdateProfileInfo(shortOp, pInstr, dTimerReadHiRes() - instrStartTime, InstrSize); #endif afterCompaction: if (Status == NO_ERR) pInstr += gPCDelta; else if (Status == CLUMP_DONE) // already requeued { pClumpRec->PC = pClumpRec->CodeStart; pClumpRec->CurrFireCount = pClumpRec->InitFireCount; return Status; } else if (Status == CLUMP_SUSPEND || Status == BREAKOUT_REQ || Status == ROTATE_QUEUE) // already requeued { pClumpRec->PC = pInstr + gPCDelta; //Throw error if we ever advance beyond the clump's codespace if (pInstr > lastClumpInstr) { NXT_BREAK; Status = ERR_INSTR; } return Status; } else if (Status == ERR_MEM) { //Memory is full. Compact dataspace and try the instruction again. //!!! Could compact DopeVectorArray here cCmdDSCompact(); if(shortOp > 7) // shortop bit set Status= ShortInterpFuncs[shortOp - 8](pInstr); else Status = (*InterpFuncs[InstrSize])(pInstr); if(Status == ERR_MEM) return Status; else goto afterCompaction; } else // other errors, breakout, stop return Status; //Throw error if we ever advance beyond the clump's codespace if (pInstr > lastClumpInstr) { NXT_BREAK; Status = ERR_INSTR; } #if VM_BENCHMARK //Increment opcode count VarsCmd.OpcodeBenchmarks[OP_CODE(pInstr)][0]++; InstrTime = dTimerRead() - InstrTime; if (InstrTime > 1) { VarsCmd.OpcodeBenchmarks[OP_CODE(pInstr)][1]++; if (InstrTime > VarsCmd.OpcodeBenchmarks[OP_CODE(pInstr)][2]) VarsCmd.OpcodeBenchmarks[OP_CODE(pInstr)][2] = InstrTime; } VarsCmd.InstrCount++; #endif //Count one more instruction for this pass if ((SLONG)(nextMSTick - dTimerReadTicks()) <= 0) // HWTimer has passed ms tick limit Status= TIMES_UP; else if(--i <= 0) Status= ROTATE_QUEUE; } while (!Status); pClumpRec->PC= pInstr; return (Status); } NXT_STATUS cCmdInterpUnop1(CODE_WORD * const pCode) { NXT_STATUS Status = NO_ERR; UBYTE opCode; DATA_ARG Arg1; NXT_ASSERT(pCode != NULL); gPCDelta= 2; opCode = OP_CODE(pCode); Arg1 = pCode[1]; switch (opCode) { case OP_JMP: { gPCDelta= (SWORD)Arg1; Status = NO_ERR; } break; case OP_ACQUIRE: { NXT_ASSERT(cCmdIsDSElementIDSane(Arg1)); NXT_ASSERT(VarsCmd.pDataspaceTOC[Arg1].TypeCode == TC_MUTEX); Status = cCmdAcquireMutex((MUTEX_Q *)cCmdDSScalarPtr(Arg1, 0)); } break; case OP_RELEASE: { NXT_ASSERT(cCmdIsDSElementIDSane(Arg1)); NXT_ASSERT(VarsCmd.pDataspaceTOC[Arg1].TypeCode == TC_MUTEX); Status = cCmdReleaseMutex((MUTEX_Q *)cCmdDSScalarPtr(Arg1, 0)); } break; case OP_SUBRET: { NXT_ASSERT(cCmdIsDSElementIDSane(Arg1)); //Take Subroutine off RunQ //Add Subroutine's caller to RunQ cCmdDeQClump(&(VarsCmd.RunQ), VarsCmd.RunQ.Head); cCmdEnQClump(&(VarsCmd.RunQ), *((CLUMP_ID *)cCmdDSScalarPtr(Arg1, 0))); Status = CLUMP_DONE; } break; case OP_FINCLUMPIMMED: { CLUMP_ID Clump= VarsCmd.RunQ.Head; // DeQ changes Head, use local val cCmdDeQClump(&(VarsCmd.RunQ), Clump); //Dequeue finalized clump cCmdSchedDependent(Clump, (CLUMP_ID)Arg1); // Use immediate form Status = CLUMP_DONE; } break; case OP_GETTICK: { cCmdSetScalarValFromDataArg(Arg1, dTimerReadNoPoll()); } break; case OP_STOP: { //Unwired Arg1 means always stop if (Arg1 == NOT_A_DS_ID) Status = STOP_REQ; else if (cCmdGetScalarValFromDataArg(Arg1, 0) > 0) Status = STOP_REQ; } break; default: { //Fatal error: Unrecognized instruction NXT_BREAK; Status = ERR_INSTR; } break; } return (Status); } ULONG scalarNots= 0, scalarBrtst= 0, scalarUn2Other= 0, scalarUn2Dispatch= 0, polyUn2Dispatch= 0; NXT_STATUS cCmdInterpScalarUnop2(CODE_WORD * const pCode) { NXT_STATUS Status; UBYTE opCode; NXT_ASSERT(pCode != NULL); opCode = OP_CODE(pCode); DATA_ARG Arg1, Arg2; scalarUn2Dispatch ++; if(opCode == OP_NOT) // t2 && t3 guaranteed scalar { gPCDelta= 3; Arg1 = pCode[1]; Arg2 = pCode[2]; ULONG ArgVal1, ArgVal2; ArgVal2= cCmdGetScalarValFromDataArg(Arg2, 0); //!!! OP_NOT is logical, *not* bit-wise. //This differs from the other logical ops because we don't distinguish booleans from UBYTEs. ArgVal1= (!ArgVal2); cCmdSetScalarValFromDataArg(Arg1, ArgVal1); Status = NO_ERR; scalarNots ++; } else if(opCode == OP_BRTST) { ULONG Branch, compare= COMP_CODE(pCode); ULONG TypeCode; Arg1 = pCode[1]; Arg2 = pCode[2]; TypeCode = cCmdDSType(Arg2); if(Arg2 == NOT_A_DS_ID) { Branch= ((compare == OPCC1_EQ) || (compare == OPCC1_LTEQ) || (compare == OPCC1_GTEQ)); } else { if(compare == OPCC1_EQ && TypeCode == TC_UBYTE) // very common for loops { UBYTE *pBRVal = (VarsCmd.pDataspace + VarsCmd.pDataspaceTOC[Arg2].DSOffset); Branch= *pBRVal == 0; } else { SLONG SVal1 = (SLONG)cCmdGetScalarValFromDataArg(Arg2, 0); Branch= ((compare == OPCC1_EQ && SVal1 == 0) || (compare == OPCC1_NEQ && SVal1 != 0) || (compare == OPCC1_GT && SVal1 > 0) || (compare == OPCC1_LT && SVal1 < 0) || (compare == OPCC1_LTEQ && SVal1 <= 0) || (compare == OPCC1_GTEQ && SVal1 >= 0)); } } if (Branch) gPCDelta = (SWORD)Arg1; else gPCDelta= 3; Status = NO_ERR; scalarBrtst ++; } else { Status= cCmdInterpUnop2(pCode); scalarUn2Other ++; } return Status; } NXT_STATUS cCmdInterpUnop2(CODE_WORD * const pCode) { NXT_STATUS Status = NO_ERR; UBYTE opCode; DATA_ARG Arg1; DATA_ARG Arg2; void *pArg1 = NULL, *pArg2 = NULL; TYPE_CODE TypeCode1, TypeCode2; ULONG i; UWORD ArgC; static UBYTE * ArgV[MAX_CALL_ARGS + 1]; polyUn2Dispatch ++; UWORD Count; UWORD Offset; SLONG TmpSLong; ULONG TmpULong; ULONG ArgVal2; float FltArgVal2; char Buffer[30]; char FormatString[5]; UBYTE CheckTrailingZeros = 0; NXT_ASSERT(pCode != NULL); gPCDelta= 3; opCode = OP_CODE(pCode); Arg1 = pCode[1]; Arg2 = pCode[2]; if (opCode == OP_NEG || opCode == OP_NOT || opCode == OP_TST || opCode == OP_SQRT || opCode == OP_ABS) { return cCmdInterpPolyUnop2(*pCode, Arg1, 0, Arg2, 0); } switch (opCode) { case OP_MOV: { Status= cCmdMove(Arg1, Arg2); } break; case OP_SET: { //!!! Should throw error if TypeCode1 is non-scalar // Accepting non-scalar destinations could have unpredictable results! cCmdSetScalarValFromDataArg(Arg1, Arg2); } break; case OP_BRTST: { //!!!BDUGGAN BRTST w/ Float? ULONG Branch, compare= COMP_CODE(pCode); ULONG TypeCode = cCmdDSType(Arg2); if(compare == OPCC1_EQ && TypeCode == TC_UBYTE) // very common for loops { UBYTE *pBRVal = (VarsCmd.pDataspace + VarsCmd.pDataspaceTOC[Arg2].DSOffset); Branch= *pBRVal == 0; } else { SLONG SVal1 = (SLONG)cCmdGetScalarValFromDataArg(Arg2, 0); Branch= ((compare == OPCC1_EQ && SVal1 == 0) || (compare == OPCC1_NEQ && SVal1 != 0) || (compare == OPCC1_GT && SVal1 > 0) || (compare == OPCC1_LT && SVal1 < 0) || (compare == OPCC1_LTEQ && SVal1 <= 0) || (compare == OPCC1_GTEQ && SVal1 >= 0)); } if (Branch) { gPCDelta = (SWORD)Arg1; Status = NO_ERR; } } break; case OP_FINCLUMP: { CLUMP_ID Clump= VarsCmd.RunQ.Head; // DeQ changes Head, use local val cCmdDeQClump(&(VarsCmd.RunQ), Clump); //Dequeue finalized clump cCmdSchedDependents(Clump, (SWORD)Arg1, (SWORD)Arg2); Status = CLUMP_DONE; } break; case OP_SUBCALL: { NXT_ASSERT(cCmdIsClumpIDSane((CLUMP_ID)Arg1)); NXT_ASSERT(!cCmdIsClumpOnQ(&(VarsCmd.RunQ), (CLUMP_ID)Arg1)); NXT_ASSERT(cCmdIsDSElementIDSane(Arg2)); *((CLUMP_ID *)(cCmdDSScalarPtr(Arg2, 0))) = VarsCmd.RunQ.Head; cCmdDeQClump(&(VarsCmd.RunQ), VarsCmd.RunQ.Head); //Take caller off RunQ cCmdEnQClump(&(VarsCmd.RunQ), (CLUMP_ID)Arg1); //Add callee to RunQ Status = CLUMP_SUSPEND; } break; case OP_ARRSIZE: { cCmdSetScalarValFromDataArg(Arg1, cCmdArrayCount(Arg2, 0)); } break; case OP_SYSCALL: { if (Arg1 >= SYSCALL_COUNT) { NXT_BREAK; Status = ERR_INSTR; break; } ArgC = cCmdClusterCount(Arg2); if (ArgC > MAX_CALL_ARGS) { NXT_BREAK; Status = ERR_INSTR; break; } if (ArgC > 0) { Arg2 = INC_ID(Arg2); for (i = 0; i < ArgC; i++) { if (cCmdDSType(Arg2) == TC_ARRAY) { //Storing pointer to array's DV_INDEX //!!! This resolve is different than cCmdDSPtr // since SysCalls may need the DVIndex to re-alloc arrays ArgV[i] = VarsCmd.pDataspace + VarsCmd.pDataspaceTOC[Arg2].DSOffset; } else { ArgV[i] = cCmdDSPtr(Arg2, 0); } //If any argument fails to resolve, return a fatal error. if (ArgV[i] == NULL) { Status = ERR_BAD_PTR; break; } Arg2 = cCmdNextDSElement(Arg2); } } else { i = 0; } //ArgV list is null terminated ArgV[i] = NULL; Status = (*SysCallFuncs[Arg1])(ArgV); } break; case OP_FLATTEN: { //Flatten Arg2 to a NULL terminated string //Assert that the destination is a string (array of bytes) NXT_ASSERT(cCmdDSType(Arg1) == TC_ARRAY); NXT_ASSERT(cCmdDSType(INC_ID(Arg1)) == TC_UBYTE); Count = cCmdCalcFlattenedSize(Arg2, 0); //Add room for NULL terminator Count++; Status = cCmdDSArrayAlloc(Arg1, 0, Count); if (IS_ERR(Status)) return Status; pArg1 = cCmdResolveDataArg(Arg1, 0, NULL); Offset = 0; Status = cCmdFlattenToByteArray(pArg1, &Offset, Arg2, 0); //Append NULL terminator *((UBYTE *)pArg1 + Offset) = 0; Offset++; NXT_ASSERT(Offset == Count); } break; case OP_NUMTOSTRING: { //Assert that the destination is a string (array of bytes) NXT_ASSERT(cCmdDSType(Arg1) == TC_ARRAY); NXT_ASSERT(cCmdDSType(INC_ID(Arg1)) == TC_UBYTE); //Make sure we're trying to convert a scalar to a string TypeCode2= cCmdDSType(Arg2); NXT_ASSERT(!IS_AGGREGATE_TYPE(TypeCode2)); if (TypeCode2 == TC_FLOAT) { pArg2 = cCmdResolveDataArg(Arg2, 0, NULL); FltArgVal2 = cCmdGetValFlt(pArg2, TypeCode2); // is number too big for display? then format differently and don't bother with trailing zeros if ((FltArgVal2 > 9999999999999.99f)||(FltArgVal2 < -999999999999.99f)){ // these are the widest %.2f numbers that will fit on display strcpy (FormatString, "%.6g"); } else{ strcpy (FormatString, "%.2f"); CheckTrailingZeros = 1; } Count = sprintf(Buffer, FormatString, FltArgVal2); Count++; //add room for null terminator if (CheckTrailingZeros){ // Determine if the trailing digits are zeros. If so, drop them if (Buffer[Count-2] == 0x30) { // NOTE: 0x30 is ASCII 0 if (Buffer[Count-3] == 0x30){ strcpy (FormatString, "%.0f"); // the last two digits = 0, copy as integer Count = Count - 3; // don't need memory for decimal and 2 ascii characters } else { strcpy (FormatString, "%.1f"); // only the 2nd digit = 0 so drop it, but keep the tenths place Count = Count - 1; // don't need memory for 2nd ascii character } } } } else { ArgVal2 = cCmdGetScalarValFromDataArg(Arg2, 0); //Calculate size of array if (ArgVal2 == 0) Count = 1; else { Count = 0; SLONG digits= 0; ULONG Tmp= 1; if (TypeCode2 == TC_SLONG || TypeCode2 == TC_SWORD || TypeCode2 == TC_SBYTE) { TmpSLong = (SLONG)ArgVal2; //Add room for negative sign if (TmpSLong < 0) { Count++; TmpULong= -TmpSLong; } else TmpULong= ArgVal2; } else TmpULong= ArgVal2; while (Tmp <= TmpULong && digits < 10) { // maxint is ten digits, max Tmp *= 10; digits++; } Count += digits; } //add room for NULL terminator Count++; } //Allocate array Status = cCmdDSArrayAlloc(Arg1, 0, Count); if (IS_ERR(Status)) return Status; pArg1 = cCmdResolveDataArg(Arg1, 0, &TypeCode1); //Populate array if (TypeCode2 == TC_FLOAT) { sprintf(pArg1, FormatString, FltArgVal2); } else if (TypeCode2 == TC_SLONG || TypeCode2 == TC_SWORD || TypeCode2 == TC_SBYTE) { sprintf(pArg1, "%d", (SLONG)ArgVal2); } else { sprintf(pArg1, "%u", ArgVal2); } } break; case OP_STRTOBYTEARR: { NXT_ASSERT((cCmdDSType(Arg1) == TC_ARRAY) && (cCmdDSType(INC_ID(Arg1)) == TC_UBYTE)); NXT_ASSERT((cCmdDSType(Arg2) == TC_ARRAY) && (cCmdDSType(INC_ID(Arg2)) == TC_UBYTE)); Count = cCmdArrayCount(Arg2, 0); if (Count > 0) { Status = cCmdDSArrayAlloc(Arg1, 0, (UWORD)(Count - 1)); if (IS_ERR(Status)) return Status; pArg1 = cCmdResolveDataArg(Arg1, 0, NULL); pArg2 = cCmdResolveDataArg(Arg2, 0, NULL); memmove(pArg1, pArg2, Count - 1); } } break; case OP_BYTEARRTOSTR: { NXT_ASSERT((cCmdDSType(Arg1) == TC_ARRAY) && (cCmdDSType(INC_ID(Arg1)) == TC_UBYTE)); NXT_ASSERT((cCmdDSType(Arg2) == TC_ARRAY) && (cCmdDSType(INC_ID(Arg2)) == TC_UBYTE)); Count = cCmdArrayCount(Arg2, 0); Status = cCmdDSArrayAlloc(Arg1, 0, (UWORD)(Count + 1)); if (IS_ERR(Status)) return Status; pArg1 = cCmdResolveDataArg(Arg1, 0, NULL); pArg2 = cCmdResolveDataArg(Arg2, 0, NULL); memmove(pArg1, pArg2, Count); *((UBYTE *)pArg1 + Count) = '\0'; } break; case OP_WAIT: { ULONG wait= 0; //Unwired Arg2 defaults to wait 0, which rotates queue if (Arg2 != NOT_A_DS_ID) wait= cCmdGetScalarValFromDataArg(Arg2, 0); if(wait == 0) Status= ROTATE_QUEUE; else Status = cCmdSleepClump(wait + IOMapCmd.Tick); // put to sleep, to wake up wait ms in future if(Arg1 != NOT_A_DS_ID) cCmdSetScalarValFromDataArg(Arg1, dTimerReadNoPoll()); } break; default: { //Fatal error: Unrecognized instruction NXT_BREAK; Status = ERR_INSTR; } break; } return (Status); } NXT_STATUS cCmdInterpPolyUnop2(CODE_WORD const Code, DATA_ARG Arg1, UWORD Offset1Param, DATA_ARG Arg2, UWORD Offset2Param) { NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode1, TypeCode2; DV_INDEX DVIndex1, DVIndex2; ULONG ArgVal1, ArgVal2; float FltArgVal1, FltArgVal2; UWORD Count1, Count2, Offset1= Offset1Param, Offset2= Offset2Param; UWORD MinArrayCount; UWORD i; //!!! AdvCluster is intended to catch the case where sources are Cluster and an Array of Clusters. // In practice, the logic it uses is broken, leading to some source cluster elements being ignored. UBYTE AdvCluster; void * pArg1 = NULL, *pArg2 = NULL; TypeCode1 = cCmdDSType(Arg1); TypeCode2 = cCmdDSType(Arg2); //Simple case, scalar. Solve and return. if (!IS_AGGREGATE_TYPE(TypeCode2)) { NXT_ASSERT(!IS_AGGREGATE_TYPE(TypeCode1)); pArg1 = cCmdResolveDataArg(Arg1, Offset1, &TypeCode1); if (TypeCode1 == TC_FLOAT || TypeCode2 == TC_FLOAT) { pArg2 = cCmdResolveDataArg(Arg2, Offset2, &TypeCode2); FltArgVal2 = cCmdGetValFlt(pArg2, TypeCode2); FltArgVal1 = cCmdUnop2Flt(Code, FltArgVal2, TypeCode2); cCmdSetValFlt(pArg1, TypeCode1, FltArgVal1); } else { ArgVal2= cCmdGetScalarValFromDataArg(Arg2, Offset2); if(OP_CODE(&Code) == OP_MOV) ArgVal1= ArgVal2; else ArgVal1 = cCmdUnop2(Code, ArgVal2, TypeCode2); cCmdSetVal(pArg1, TypeCode1, ArgVal1); } return Status; } //At least one of the args is an aggregate type if(TypeCode1 == TC_ARRAY && TypeCode2 == TC_ARRAY) { TYPE_CODE tc1, tc2; tc1= cCmdDSType(INC_ID(Arg1)); tc2= cCmdDSType(INC_ID(Arg2)); if(tc1 <= TC_LAST_INT_SCALAR && tc1 == tc2) { void *pArg1, *pArg2; ULONG Count = cCmdArrayCount(Arg2, Offset2); Status = cCmdDSArrayAlloc(Arg1, Offset1, Count); if (IS_ERR(Status)) return Status; pArg1 = cCmdResolveDataArg(Arg1, Offset1, NULL); pArg2 = cCmdResolveDataArg(Arg2, Offset2, NULL); memmove(pArg1, pArg2, Count * cCmdSizeOf(tc1)); return Status; } } // // Initialize Count and ArrayType local variables for each argument // if (TypeCode2 == TC_ARRAY) { Count2 = cCmdArrayCount(Arg2, Offset2); DVIndex2 = cCmdGetDVIndex(Arg2, Offset2); Offset2 = DV_ARRAY[DVIndex2].Offset; } else if (TypeCode2 == TC_CLUSTER) { Count2 = cCmdClusterCount(Arg2); } if (TypeCode1 == TC_ARRAY) { if (TypeCode2 != TC_ARRAY) { //If output is an array, but source is not an array, that's a fatal error! NXT_BREAK; return (ERR_ARG); } if(Count2 == 0) { // both arrays, input is empty, is output already empty? Count1= cCmdArrayCount(Arg1, Offset1); if(Count1 == 0) return NO_ERR; } MinArrayCount = Count2; //Make sure the destination array is the proper size to hold the result Status = cCmdDSArrayAlloc(Arg1, Offset1, MinArrayCount); if (IS_ERR(Status)) return Status; if(MinArrayCount == 0) // if we emptied array, nothing else to do. return NO_ERR; Count1 = MinArrayCount; DVIndex1 = cCmdGetDVIndex(Arg1, Offset1); Offset1 = DV_ARRAY[DVIndex1].Offset; AdvCluster = FALSE; } else if (TypeCode1 == TC_CLUSTER) { Count1 = cCmdClusterCount(Arg1); AdvCluster = TRUE; } //Advance aggregate args to first sub-element for next call if (IS_AGGREGATE_TYPE(TypeCode1)) Arg1 = INC_ID(Arg1); if (IS_AGGREGATE_TYPE(TypeCode2)) Arg2 = INC_ID(Arg2); // // Loop through the sub-elements of aggregate arguments. // Call cCmdInterpPolyUnop2 recursively with simpler type. // for (i = 0; i < Count1; i++) { Status = cCmdInterpPolyUnop2(Code, Arg1, Offset1, Arg2, Offset2); if (IS_ERR(Status)) return Status; //Advance aggregate args to next sub-element if (TypeCode1 == TC_ARRAY) Offset1 += DV_ARRAY[DVIndex1].ElemSize; else if ((TypeCode1 == TC_CLUSTER) && AdvCluster) Arg1 = cCmdNextDSElement(Arg1); if (TypeCode2 == TC_ARRAY) Offset2 += DV_ARRAY[DVIndex2].ElemSize; else if ((TypeCode2 == TC_CLUSTER) && AdvCluster) Arg2 = cCmdNextDSElement(Arg2); } return Status; } ULONG cCmdUnop2(CODE_WORD const Code, ULONG Operand, TYPE_CODE TypeCode) { UBYTE opCode; opCode = OP_CODE((&Code)); if(opCode == OP_MOV) return Operand; else if(opCode == OP_NEG) return (-((SLONG)Operand)); else if(opCode == OP_NOT) //!!! OP_NOT is logical, *not* bit-wise. //This differs from the other logical ops because we don't distinguish booleans from UBYTEs. return (!Operand); else if(opCode == OP_TST) return cCmdCompare(COMP_CODE((&Code)), Operand, 0, TypeCode, TypeCode); else if(opCode == OP_ABS) return abs(Operand); else { //Unrecognized instruction, NXT_BREAK for easy debugging (ERR_INSTR handled in caller) NXT_BREAK; return 0; } } float cCmdUnop2Flt(CODE_WORD const Code, float Operand, TYPE_CODE TypeCode) { UBYTE opCode; opCode = OP_CODE((&Code)); if(opCode == OP_MOV) return Operand; else if(opCode == OP_NEG) return (-(Operand)); else if(opCode == OP_NOT) //!!! OP_NOT is logical, *not* bit-wise. //This differs from the other logical ops because we don't distinguish booleans from UBYTEs. return (!Operand); else if(opCode == OP_TST) return cCmdCompareFlt(COMP_CODE((&Code)), Operand, 0, TypeCode, TypeCode); else if(opCode == OP_ABS) return fabsf(Operand); else if(opCode == OP_SQRT) return sqrtf(Operand); #if 0 else if(opCode == OP_SIN) return sinf(Operand); else if(opCode == OP_COS) return cosf(Operand); else if(opCode == OP_TAN) return tanf(Operand); else if(opCode == OP_ASIN) return asinf(Operand); else if(opCode == OP_ACOS) return acosf(Operand); else if(opCode == OP_ATAN) return atanf(Operand); #endif else { //Unrecognized instruction, NXT_BREAK for easy debugging (ERR_INSTR handled in caller) NXT_BREAK; return 0; } } NXT_STATUS cCmdIOGetSet(ULONG opCode, DATA_ARG Arg1, DATA_ARG Arg2, DATA_ARG Arg3) { ULONG ArgVal1, ArgVal2; TYPE_CODE TypeCode2; void *pArg2 = NULL; switch(opCode) { case OP_GETOUT: { ArgVal2 = cCmdGetScalarValFromDataArg(Arg2, 0); Arg2 = (UWORD)(0x200 | (Arg3 + ArgVal2 * IO_OUT_FPP)); pArg2 = cCmdResolveIODataArg(Arg2, 0, &TypeCode2); cCmdSetScalarValFromDataArg(Arg1, cCmdGetVal(pArg2, TypeCode2)); } break; //!!! All IO map access commands should screen illegal port values! // Right now, cCmdResolveIODataArg's implementation allows SETIN/GETIN to access arbitrary RAM! case OP_SETIN: { ArgVal2 = cCmdGetScalarValFromDataArg(Arg2, 0); Arg2 = (UWORD)(Arg3 + ArgVal2 * IO_IN_FPP); pArg2 = cCmdResolveIODataArg(Arg2, 0, &TypeCode2); ArgVal1 = cCmdGetScalarValFromDataArg(Arg1, 0); cCmdSetVal(pArg2, TypeCode2, ArgVal1); } break; case OP_GETIN: { TYPE_CODE TypeCode1; void * pArg1; ArgVal2 = cCmdGetScalarValFromDataArg(Arg2, 0); Arg2 = (UWORD)(Arg3 + ArgVal2 * IO_IN_FPP); pArg2 = cCmdResolveIODataArg(Arg2, 0, &TypeCode2); TypeCode1= cCmdDSType(Arg1); pArg1= cCmdDSScalarPtr(Arg1, 0); if(TypeCode1 <= TC_SBYTE && TypeCode1 <= TC_SBYTE) // seems really common *(UBYTE*)pArg1= *(UBYTE*)pArg2; else cCmdSetVal(pArg1, TypeCode1, cCmdGetVal(pArg2, TypeCode2)); } break; } return NO_ERR; } ULONG scalarCmp= 0, scalarFloatCmp= 0, recursiveCmp= 0, PolyScalarCmp= 0, polyPolyCmp= 0, scalarOther= 0, scalarBinopDispatch= 0, polyBinopDispatch= 0; NXT_STATUS cCmdInterpScalarBinop(CODE_WORD * const pCode) { NXT_STATUS Status; UBYTE opCode; UBYTE CmpBool; NXT_ASSERT(pCode != NULL); opCode = OP_CODE(pCode); DATA_ARG Arg1, Arg2, Arg3; scalarBinopDispatch ++; if(opCode == OP_CMP) // t2 && t3 guaranteed scalar or string { gPCDelta= 4; Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; ULONG ArgVal1, ArgVal2, ArgVal3; TYPE_CODE TypeCode2, TypeCode3; DS_TOC_ENTRY *dsTOC2Ptr= &VarsCmd.pDataspaceTOC[Arg2]; DS_TOC_ENTRY *dsTOC3Ptr= &VarsCmd.pDataspaceTOC[Arg3]; TypeCode2 = dsTOC2Ptr->TypeCode; TypeCode3 = dsTOC3Ptr->TypeCode; if(TypeCode2 <= TC_LAST_INT_SCALAR && TypeCode3 <= TC_LAST_INT_SCALAR) { ArgVal2= GetProcArray[TypeCode2](VarsCmd.pDataspace + dsTOC2Ptr->DSOffset); ArgVal3= GetProcArray[TypeCode3](VarsCmd.pDataspace + dsTOC3Ptr->DSOffset); ArgVal1= cCmdCompare(COMP_CODE(pCode), ArgVal2, ArgVal3, TypeCode2, TypeCode3); DS_TOC_ENTRY *dsTOC1Ptr= &VarsCmd.pDataspaceTOC[Arg1]; SetProcArray[dsTOC1Ptr->TypeCode](VarsCmd.pDataspace + dsTOC1Ptr->DSOffset, ArgVal1); scalarCmp++; Status = NO_ERR; } else if (TypeCode2 == TC_ARRAY) // two strings { // memcmp(); here or in compareagg, could use memcmp to speed up string compares ??? Status = cCmdCompareAggregates(COMP_CODE(pCode), &CmpBool, Arg2, 0, Arg3, 0); cCmdSetScalarValFromDataArg(Arg1, CmpBool); recursiveCmp++; } else { // floats Status = cCmdInterpPolyBinop(*pCode, Arg1, 0, Arg2, 0, Arg3, 0); scalarFloatCmp++; } } else if(opCode == OP_BRCMP) { // t2 and t3 guaranteed scalar TYPE_CODE TypeCode2, TypeCode3; ULONG ArgVal2, ArgVal3; Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; TypeCode2= cCmdDSType(Arg2); TypeCode3= cCmdDSType(Arg3); ArgVal2= cCmdGetScalarValFromDataArg(Arg2, 0); ArgVal3= cCmdGetScalarValFromDataArg(Arg3, 0); CmpBool= cCmdCompare(COMP_CODE(pCode), ArgVal2, ArgVal3, TypeCode2, TypeCode3); if (CmpBool) gPCDelta = (SWORD)Arg1; else gPCDelta= 4; Status= NO_ERR; } else if(opCode >= OP_SETIN && opCode <= OP_GETOUT) { Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; Status= cCmdIOGetSet(opCode, Arg1, Arg2, Arg3); gPCDelta= 4; } else { scalarOther ++; Status= cCmdInterpBinop(pCode); } return Status; } NXT_STATUS cCmdInterpBinop(CODE_WORD * const pCode) { NXT_STATUS Status = NO_ERR; UBYTE opCode; DATA_ARG Arg1, Arg2, Arg3; ULONG ArgVal3; UBYTE CmpBool; DV_INDEX DVIndex1, DVIndex2; UWORD i; polyBinopDispatch ++; gPCDelta= 4; NXT_ASSERT(pCode != NULL); opCode = OP_CODE(pCode); Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; if (opCode <= OP_XOR) // && ! OP_NEG, can't happen since it is unop Status= cCmdInterpPolyBinop(opCode, Arg1, 0, Arg2, 0, Arg3, 0); else if(opCode >= OP_SETIN && opCode <= OP_GETOUT) Status= cCmdIOGetSet(opCode, Arg1, Arg2, Arg3); else { switch (opCode) { case OP_CMP: { TYPE_CODE TypeCode2= cCmdDSType(Arg2), TypeCode3= cCmdDSType(Arg3); if(TypeCode2 <= TC_LAST_INT_SCALAR && TypeCode3 <= TC_LAST_INT_SCALAR) { ULONG ArgVal1, ArgVal2, ArgVal3; ArgVal2= cCmdGetScalarValFromDataArg(Arg2, 0); ArgVal3= cCmdGetScalarValFromDataArg(Arg3, 0); ArgVal1= cCmdCompare(COMP_CODE(pCode), ArgVal2, ArgVal3, TypeCode2, TypeCode3); cCmdSetScalarValFromDataArg(Arg1, ArgVal1); PolyScalarCmp++; } else if (IS_AGGREGATE_TYPE(TypeCode2) && IS_AGGREGATE_TYPE(TypeCode3) && !IS_AGGREGATE_TYPE(cCmdDSType(Arg1))) { //Compare Aggregates Status = cCmdCompareAggregates(COMP_CODE(pCode), &CmpBool, Arg2, 0, Arg3, 0); cCmdSetScalarValFromDataArg(Arg1, CmpBool); recursiveCmp++; } else { //Compare Elements Status = cCmdInterpPolyBinop(*pCode, Arg1, 0, Arg2, 0, Arg3, 0); polyPolyCmp++; } } break; case OP_BRCMP: { TYPE_CODE TypeCode2= cCmdDSType(Arg2), TypeCode3= cCmdDSType(Arg3); if(TypeCode2 <= TC_LAST_INT_SCALAR && TypeCode3 <= TC_LAST_INT_SCALAR) { ULONG ArgVal2, ArgVal3; ArgVal2= cCmdGetScalarValFromDataArg(Arg2, 0); ArgVal3= cCmdGetScalarValFromDataArg(Arg3, 0); CmpBool= cCmdCompare(COMP_CODE(pCode), ArgVal2, ArgVal3, TypeCode2, TypeCode3); } else //Compare Aggregates Status = cCmdCompareAggregates(COMP_CODE(pCode), &CmpBool, Arg2, 0, Arg3, 0); if (CmpBool) gPCDelta = (SWORD)Arg1; } break; case OP_INDEX: { ArgVal3 = (Arg3 != NOT_A_DS_ID) ? cCmdGetScalarValFromDataArg(Arg3, 0) : 0; DVIndex2 = cCmdGetDVIndex(Arg2, 0); if (ArgVal3 >= DV_ARRAY[DVIndex2].Count) return (ERR_ARG); Status = cCmdInterpPolyUnop2(OP_MOV, Arg1, 0, INC_ID(Arg2), ARRAY_ELEM_OFFSET(DVIndex2, ArgVal3)); } break; case OP_ARRINIT: { //Arg1 - Dst, Arg2 - element type/default val, Arg3 - length NXT_ASSERT(cCmdDSType(Arg1) == TC_ARRAY); ArgVal3 = (Arg3 != NOT_A_DS_ID) ? cCmdGetScalarValFromDataArg(Arg3, 0) : 0; Status = cCmdDSArrayAlloc(Arg1, 0, (UWORD)ArgVal3); if (!IS_ERR(Status)) { DVIndex1 = cCmdGetDVIndex(Arg1, 0); if(cCmdDSType(Arg2) <= TC_LAST_INT_SCALAR) { ULONG val= cCmdGetScalarValFromDataArg(Arg2, 0); TYPE_CODE TypeCode= cCmdDSType(INC_ID(Arg1)); for (i = 0; i < ArgVal3; i++) // could init ptr and incr by offset GM??? { //copy Arg2 into each element of Arg1 cCmdSetVal(VarsCmd.pDataspace + ARRAY_ELEM_OFFSET(DVIndex1, i), TypeCode, val); } } else for (i = 0; i < ArgVal3; i++) //copy Arg2 into each element of Arg1 Status = cCmdInterpPolyUnop2(OP_MOV, INC_ID(Arg1), ARRAY_ELEM_OFFSET(DVIndex1, i), Arg2, 0); } } break; default: { //Fatal error: Unrecognized instruction NXT_BREAK; Status = ERR_INSTR; } break; } } return (Status); } NXT_STATUS cCmdInterpPolyBinop(CODE_WORD const Code, DATA_ARG Arg1, UWORD Offset1, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3) { NXT_STATUS Status = NO_ERR; TYPE_CODE TypeCode1, TypeCode2, TypeCode3; DV_INDEX DVIndex1, DVIndex2, DVIndex3; ULONG ArgVal1, ArgVal2, ArgVal3; float FltArgVal1, FltArgVal2, FltArgVal3; UWORD Count1, Count2, Count3; UWORD MinArrayCount; UWORD i; //!!! AdvCluster is intended to catch the case where sources are Cluster and an Array of Clusters. // In practice, the logic it uses is broken, leading to some source cluster elements being ignored. UBYTE AdvCluster; void * pArg1 = NULL, *pArg2 = NULL, *pArg3 = NULL; TypeCode1 = cCmdDSType(Arg1); TypeCode2 = cCmdDSType(Arg2); TypeCode3 = cCmdDSType(Arg3); //Simple case, both args are scalars. Solve and return. if ((!IS_AGGREGATE_TYPE(TypeCode2)) && (!IS_AGGREGATE_TYPE(TypeCode3))) { NXT_ASSERT(!IS_AGGREGATE_TYPE(TypeCode1)); pArg1 = cCmdResolveDataArg(Arg1, Offset1, NULL); if (TypeCode1 == TC_FLOAT || TypeCode2 == TC_FLOAT || TypeCode3 == TC_FLOAT){ pArg2 = cCmdResolveDataArg(Arg2, Offset2, NULL); pArg3 = cCmdResolveDataArg(Arg3, Offset3, NULL); FltArgVal2 = cCmdGetValFlt(pArg2, TypeCode2); FltArgVal3 = cCmdGetValFlt(pArg3, TypeCode3); FltArgVal1 = cCmdBinopFlt(Code, FltArgVal2, FltArgVal3, TypeCode2, TypeCode3); cCmdSetValFlt(pArg1, TypeCode1, FltArgVal1); } else { ArgVal2 = cCmdGetScalarValFromDataArg(Arg2, Offset2); ArgVal3 = cCmdGetScalarValFromDataArg(Arg3, Offset3); ArgVal1 = cCmdBinop(Code, ArgVal2, ArgVal3, TypeCode2, TypeCode3); cCmdSetVal(pArg1, TypeCode1, ArgVal1); } return Status; } //At least one of the args is an aggregate type // // Initialize Count and ArrayType local variables for each argument // if (TypeCode2 == TC_ARRAY) { Count2 = cCmdArrayCount(Arg2, Offset2); DVIndex2 = cCmdGetDVIndex(Arg2, Offset2); Offset2 = DV_ARRAY[DVIndex2].Offset; } else if (TypeCode2 == TC_CLUSTER) { Count2 = cCmdClusterCount(Arg2); } if (TypeCode3 == TC_ARRAY) { Count3 = cCmdArrayCount(Arg3, Offset3); DVIndex3 = cCmdGetDVIndex(Arg3, Offset3); Offset3 = DV_ARRAY[DVIndex3].Offset; } else if (TypeCode3 == TC_CLUSTER) { Count3 = cCmdClusterCount(Arg3); } if (TypeCode1 == TC_ARRAY) { //If the destination is an array, make sure it has enough memory to hold the result if ((TypeCode2 == TC_ARRAY) && (TypeCode3 == TC_ARRAY)) { if (Count2 < Count3) MinArrayCount = Count2; else MinArrayCount = Count3; } else if (TypeCode2 == TC_ARRAY) MinArrayCount = Count2; else if (TypeCode3 == TC_ARRAY) MinArrayCount = Count3; else { //If output is an array, but no sources are arrays, that's a fatal error! NXT_BREAK; return (ERR_ARG); } //Make sure the destination array is the proper size to hold the result Status = cCmdDSArrayAlloc(Arg1, Offset1, MinArrayCount); if (IS_ERR(Status)) return Status; Count1 = MinArrayCount; DVIndex1 = cCmdGetDVIndex(Arg1, Offset1); Offset1 = DV_ARRAY[DVIndex1].Offset; AdvCluster = FALSE; } else if (TypeCode1 == TC_CLUSTER) { Count1 = cCmdClusterCount(Arg1); AdvCluster = TRUE; } //Advance aggregate args to first sub-element for next call if (IS_AGGREGATE_TYPE(TypeCode1)) Arg1 = INC_ID(Arg1); if (IS_AGGREGATE_TYPE(TypeCode2)) Arg2 = INC_ID(Arg2); if (IS_AGGREGATE_TYPE(TypeCode3)) Arg3 = INC_ID(Arg3); // // Loop through the sub-elements of aggregate arguments. // Call cCmdInterpPolyBinop recursively with simpler type. // for (i = 0; i < Count1; i++) { Status = cCmdInterpPolyBinop(Code, Arg1, Offset1, Arg2, Offset2, Arg3, Offset3); if (IS_ERR(Status)) return Status; //Advance aggregate args to next sub-element if (TypeCode1 == TC_ARRAY) Offset1 += DV_ARRAY[DVIndex1].ElemSize; else if ((TypeCode1 == TC_CLUSTER) && AdvCluster) Arg1 = cCmdNextDSElement(Arg1); if (TypeCode2 == TC_ARRAY) Offset2 += DV_ARRAY[DVIndex2].ElemSize; else if ((TypeCode2 == TC_CLUSTER) && AdvCluster) Arg2 = cCmdNextDSElement(Arg2); if (TypeCode3 == TC_ARRAY) Offset3 += DV_ARRAY[DVIndex3].ElemSize; else if ((TypeCode3 == TC_CLUSTER) && AdvCluster) Arg3 = cCmdNextDSElement(Arg3); } return Status; } ULONG cCmdBinop(CODE_WORD const Code, ULONG LeftOp, ULONG RightOp, TYPE_CODE LeftType, TYPE_CODE RightType) { UBYTE opCode; opCode = OP_CODE((&Code)); switch (opCode) { case OP_ADD: { return LeftOp + RightOp; } case OP_SUB: { return LeftOp - RightOp; } case OP_MUL: { return LeftOp * RightOp; } case OP_DIV: { //Catch divide-by-zero for a portable, well-defined result. //(x / 0) = 0. Thus Spake LOTHAR!! (It's technical.) if (RightOp == 0) return 0; if (IS_SIGNED_TYPE(LeftType) && IS_SIGNED_TYPE(RightType)) return ((SLONG)LeftOp) / ((SLONG)RightOp); else if (IS_SIGNED_TYPE(LeftType)) return ((SLONG)LeftOp) / RightOp; else if (IS_SIGNED_TYPE(RightType)) return LeftOp / ((SLONG)RightOp); else return LeftOp / RightOp; } case OP_MOD: { //As with OP_DIV, make sure (x % 0) = x is well-defined if (RightOp == 0) return (LeftOp); if (IS_SIGNED_TYPE(LeftType) && IS_SIGNED_TYPE(RightType)) return ((SLONG)LeftOp) % ((SLONG)RightOp); else if (IS_SIGNED_TYPE(LeftType)) return ((SLONG)LeftOp) % RightOp; else if (IS_SIGNED_TYPE(RightType)) return LeftOp % ((SLONG)RightOp); else return LeftOp % RightOp; } case OP_AND: { return (LeftOp & RightOp); } case OP_OR: { return (LeftOp | RightOp); } case OP_XOR: { return ((LeftOp | RightOp) & (~(LeftOp & RightOp))); } case OP_CMP: { return cCmdCompare(COMP_CODE((&Code)), LeftOp, RightOp, LeftType, RightType); } default: { //Unrecognized instruction, NXT_BREAK for easy debugging (ERR_INSTR handled in caller) NXT_BREAK; return 0; } } } float cCmdBinopFlt(CODE_WORD const Code, float LeftOp, float RightOp, TYPE_CODE LeftType, TYPE_CODE RightType) { UBYTE opCode; opCode = OP_CODE((&Code)); switch (opCode) { case OP_ADD: { return LeftOp + RightOp; } case OP_SUB: { return LeftOp - RightOp; } case OP_MUL: { return LeftOp * RightOp; } case OP_DIV: { //Catch divide-by-zero for a portable, well-defined result. //(x / 0) = 0. Thus Spake LOTHAR!! (It's technical.) if (RightOp == 0) return 0; return LeftOp / RightOp; } case OP_MOD: { //As with OP_DIV, make sure (x % 0) = x is well-defined if (RightOp == 0) return (LeftOp); return (SLONG)LeftOp % (SLONG)RightOp; } case OP_AND: { return ((SLONG)LeftOp & (SLONG)RightOp); } case OP_OR: { return ((SLONG)LeftOp | (SLONG)RightOp); } case OP_XOR: { return (((SLONG)LeftOp | (SLONG)RightOp) & (~((SLONG)LeftOp & (SLONG)RightOp))); } case OP_CMP: { return cCmdCompareFlt(COMP_CODE((&Code)), LeftOp, RightOp, LeftType, RightType); } default: { //Unrecognized instruction, NXT_BREAK for easy debugging (ERR_INSTR handled in caller) NXT_BREAK; return 0; } } } NXT_STATUS cCmdInterpNoArg(CODE_WORD * const pCode) { //Fatal error: Unrecognized instruction (no current opcodes have zero instructions) NXT_BREAK; return (ERR_INSTR); } NXT_STATUS cCmdInterpShortError(CODE_WORD * const pCode) { //Fatal error: Unrecognized instruction (no current opcodes have zero instructions) NXT_BREAK; return (ERR_INSTR); } NXT_STATUS cCmdInterpShortSubCall(CODE_WORD * const pCode) { NXT_STATUS Status; DATA_ARG Arg1, Arg2; gPCDelta= 2; Arg1 = GetDataArg(SHORT_ARG(pCode) + pCode[1]); Arg2 = GetDataArg(pCode[1]); NXT_ASSERT(cCmdIsClumpIDSane((CLUMP_ID)Arg1)); NXT_ASSERT(!cCmdIsClumpOnQ(&(VarsCmd.RunQ), (CLUMP_ID)Arg1)); NXT_ASSERT(cCmdIsDSElementIDSane(Arg2)); *((CLUMP_ID *)(cCmdDSScalarPtr(Arg2, 0))) = VarsCmd.RunQ.Head; cCmdDeQClump(&(VarsCmd.RunQ), VarsCmd.RunQ.Head); //Take caller off RunQ cCmdEnQClump(&(VarsCmd.RunQ), (CLUMP_ID)Arg1); //Add callee to RunQ Status = CLUMP_SUSPEND; return Status; } ULONG moveSameInt= 0, moveDiffInt= 0, moveFloat= 0, moveIntFloat= 0, moveFloatInt= 0, moveArrInt= 0, moveOther= 0; NXT_STATUS cCmdMove(DATA_ARG Arg1, DATA_ARG Arg2) { NXT_STATUS Status; DS_TOC_ENTRY *TOC1Ptr= &VarsCmd.pDataspaceTOC[Arg1], *TOC2Ptr= &VarsCmd.pDataspaceTOC[Arg2]; TYPE_CODE tc1= TOC1Ptr->TypeCode, tc2= TOC2Ptr->TypeCode; void *pArg1, *pArg2; if(tc1 <= TC_LAST_INT_SCALAR && tc2 <= TC_LAST_INT_SCALAR) { // if tc1 == tc2, do long, byte, then word assignment if(tc1 == tc2) { moveSameInt++; pArg1= VarsCmd.pDataspace + TOC1Ptr->DSOffset; pArg2= VarsCmd.pDataspace + TOC2Ptr->DSOffset; if(tc1 >= TC_ULONG) *(ULONG*)pArg1= *(ULONG*)pArg2; else if(tc1 <= TC_SBYTE) *(UBYTE*)pArg1= *(UBYTE*)pArg2; else *(UWORD*)pArg1= *(UWORD*)pArg2; Status= NO_ERR; } else { moveDiffInt++; ULONG val= cCmdGetScalarValFromDataArg(Arg2, 0); cCmdSetScalarValFromDataArg(Arg1, val); Status= NO_ERR; } } else if(tc1 == TC_FLOAT && tc2 == TC_FLOAT) { // float to float moveFloat++; pArg1= VarsCmd.pDataspace + TOC1Ptr->DSOffset; pArg2= VarsCmd.pDataspace + TOC2Ptr->DSOffset; *(float*)pArg1= *(float*)pArg2; Status= NO_ERR; } else if(tc1 == TC_FLOAT && tc2 <= TC_LAST_INT_SCALAR) { // int to float moveIntFloat++; pArg1= VarsCmd.pDataspace + TOC1Ptr->DSOffset; pArg2= VarsCmd.pDataspace + TOC2Ptr->DSOffset; if (tc2 == TC_SLONG) *(float*)pArg1 = *(SLONG*)pArg2; else if (tc2 == TC_ULONG) *(float*)pArg1 = *(ULONG*)pArg2; else if (tc2 == TC_SBYTE) *(float*)pArg1 = *(SBYTE*)pArg2; else if (tc2 == TC_UBYTE) *(float*)pArg1 = *(UBYTE*)pArg2; else if (tc2 == TC_UWORD) *(float*)pArg1 = *(UWORD*)pArg2; else *(float*)pArg1= *(SWORD*)pArg2; Status= NO_ERR; } else if(tc2 == TC_FLOAT && tc1 <= TC_LAST_INT_SCALAR) { // float to int moveFloatInt++; /* * According to C standard, converting a float to integer is implementation * defined when the number is out of integer bounds. * * To match original LEGO firmware, when casting float as integer, always * convert to signed value, then cast as destination type. */ pArg1= VarsCmd.pDataspace + TOC1Ptr->DSOffset; pArg2= VarsCmd.pDataspace + TOC2Ptr->DSOffset; if (tc1 == TC_SLONG || tc1 == TC_ULONG) *(SLONG*)pArg1 = (SLONG)*(float*)pArg2; else if (tc1 == TC_SBYTE || tc1 == TC_UBYTE) *(SBYTE*)pArg1 = (SLONG)*(float*)pArg2; else *(SWORD*)pArg1 = (SLONG)*(float*)pArg2; Status= NO_ERR; } //!!! Optimized move for arrays of ints. else if ((tc1 == TC_ARRAY) && (tc2 == TC_ARRAY) && ((TOC1Ptr+1)->TypeCode <= TC_LAST_INT_SCALAR) && ((TOC1Ptr+1)->TypeCode == (TOC2Ptr+1)->TypeCode)) { ULONG Count; moveArrInt++; Count = cCmdArrayCount(Arg2, 0); Status = cCmdDSArrayAlloc(Arg1, 0, Count); if (IS_ERR(Status)) return Status; pArg1 = cCmdResolveDataArg(Arg1, 0, NULL); pArg2 = cCmdResolveDataArg(Arg2, 0, NULL); memmove(pArg1, pArg2, Count * cCmdSizeOf((TOC1Ptr+1)->TypeCode)); } else { // if ((tc1 == TC_CLUSTER) && (tc2 == TC_CLUSTER)) moveOther++; Status = cCmdInterpPolyUnop2(OP_MOV, Arg1, 0, Arg2, 0); } return Status; } NXT_STATUS cCmdInterpShortMove(CODE_WORD * const pCode) { NXT_STATUS Status; DATA_ARG Arg1, Arg2; Arg1 = GetDataArg(SHORT_ARG(pCode) + pCode[1]); Arg2 = GetDataArg(pCode[1]); Status= cCmdMove(Arg1, Arg2); gPCDelta= 2; return Status; } NXT_STATUS cCmdInterpShortAcquire(CODE_WORD * const pCode) { NXT_STATUS Status; DATA_ARG Arg1; gPCDelta= 1; Arg1 = GetDataArg(SHORT_ARG(pCode)); NXT_ASSERT(cCmdIsDSElementIDSane(Arg1)); NXT_ASSERT(cCmdDSType(Arg1) == TC_MUTEX); Status = cCmdAcquireMutex((MUTEX_Q *)cCmdDSScalarPtr(Arg1, 0)); return Status; } NXT_STATUS cCmdInterpShortRelease(CODE_WORD * const pCode) { NXT_STATUS Status; DATA_ARG Arg1; gPCDelta= 1; Arg1 = GetDataArg(SHORT_ARG(pCode)); NXT_ASSERT(cCmdIsDSElementIDSane(Arg1)); NXT_ASSERT(cCmdDSType(Arg1) == TC_MUTEX); Status = cCmdReleaseMutex((MUTEX_Q *)cCmdDSScalarPtr(Arg1, 0)); return Status; } //OP_SETOUT gets it's own interpreter function because it is relatively complex // (called from cCmdInterpOther()) //This also serves as a convenient breakpoint stop for investigating output module behavior NXT_STATUS cCmdExecuteSetOut(CODE_WORD * const pCode) { TYPE_CODE TypeCodeField, TypeCodeSrc, TypeCodePortArg; void *pField = NULL, *pSrc = NULL, *pPort = NULL; DS_ELEMENT_ID PortArg; UWORD PortCount, InstrSize; ULONG Port, FieldTableIndex, i, j; DV_INDEX DVIndex; //Arg1 = InstrSize //Arg2 = port number or list of ports //Arg3 and beyond = FieldID, src DSID tuples //Calculate number of tuples //!!! Might want to throw ERR_INSTR if instrSize and tuples don't check out InstrSize = (pCode[1] / 2); //Second argument may specify a single port or an array list. //Figure out which and resolve accordingly. PortArg = pCode[2]; TypeCodePortArg = cCmdDSType(PortArg); if (TypeCodePortArg == TC_ARRAY) { DVIndex = cCmdGetDVIndex(PortArg, 0); PortCount = cCmdArrayCount(PortArg, 0); } else PortCount = 1; //For each port, process all the tuples for (i = 0; i < PortCount; i++) { if (TypeCodePortArg == TC_ARRAY) { pPort = (UBYTE*)cCmdResolveDataArg(INC_ID(PortArg), ARRAY_ELEM_OFFSET(DVIndex, i), NULL); Port = cCmdGetVal(pPort, cCmdDSType(INC_ID(PortArg))); } else { Port = cCmdGetScalarValFromDataArg(PortArg, 0); } //If user specified a valid port, process the tuples. Else, this port is a no-op if (Port < NO_OF_OUTPUTS) { for (j = 3; j < InstrSize; j += 2) { FieldTableIndex = (Port * IO_OUT_FPP) + pCode[j]; pSrc = cCmdResolveDataArg(pCode[j + 1], 0, &TypeCodeSrc); //If FieldTableIndex is valid, go ahead and set the value if (FieldTableIndex < IO_OUT_FIELD_COUNT) { pField = IO_PTRS[MOD_OUTPUT][FieldTableIndex]; TypeCodeField = IO_TYPES[MOD_OUTPUT][FieldTableIndex]; cCmdSetVal(pField, TypeCodeField, cCmdGetVal(pSrc, TypeCodeSrc)); } //Else, compiler is nutso! Return fatal error. else return (ERR_INSTR); } } } return (NO_ERR); } NXT_STATUS cCmdInterpOther(CODE_WORD * const pCode) { NXT_STATUS Status = NO_ERR; UBYTE opCode; DATA_ARG Arg1, Arg2, Arg3, Arg4, Arg5; TYPE_CODE TypeCode1, TypeCode2, TypeCode3, TypeCode5; ULONG ArgVal2, ArgVal3, ArgVal4, ArgVal5; UWORD ArrayCount1, ArrayCount2, ArrayCount3, ArrayCount4; UWORD MinCount; UWORD i,j; DV_INDEX DVIndex1, DVIndex2, DVIndex4,TmpDVIndex; UWORD SrcCount; DS_ELEMENT_ID TmpDSID; UWORD DstIndex; UWORD Size; UWORD Offset; void *pArg1 = NULL; void *pArg2 = NULL; void *pArg3 = NULL; void *pArg5 = NULL; NXT_ASSERT(pCode != NULL); ULONG sz= INSTR_SIZE(*(UWORD*)pCode); if (sz == VAR_INSTR_SIZE) sz = ((UWORD*)pCode)[1]; gPCDelta= sz/2; // advance words, sz is in bytes opCode = OP_CODE(pCode); switch (opCode) { case OP_REPLACE: { //Arg1 - Dst //Arg2 - Src //Arg3 - Index //Arg4 - New val / array of vals Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; Arg4 = pCode[4]; NXT_ASSERT(cCmdDSType(Arg1) == TC_ARRAY); NXT_ASSERT(cCmdDSType(Arg2) == TC_ARRAY); //Copy Src to Dst //!!! Could avoid full data copy if we knew which portion to overwrite if (Arg1 != Arg2) { Status= cCmdMove(Arg1, Arg2); if (IS_ERR(Status)) return Status; } DVIndex1 = cCmdGetDVIndex(Arg1, 0); //Copy new val to Dst if (Arg3 != NOT_A_DS_ID) { pArg3 = cCmdResolveDataArg(Arg3, 0, &TypeCode3); ArgVal3 = cCmdGetVal(pArg3, TypeCode3); } else { //Index input unwired ArgVal3 = 0; } ArrayCount1 = cCmdArrayCount(Arg1, 0); //Bounds check //If array index (ArgVal3) is out of range, just pass out the copy of Src (effectively no-op) if (ArgVal3 >= ArrayCount1) return (NO_ERR); if (cCmdDSType(Arg4) != TC_ARRAY) { Status = cCmdInterpPolyUnop2(OP_MOV, INC_ID(Arg1), ARRAY_ELEM_OFFSET(DVIndex1, ArgVal3), Arg4, 0); if (IS_ERR(Status)) return Status; } else { DVIndex4 = cCmdGetDVIndex(Arg4, 0); ArrayCount4 = cCmdArrayCount(Arg4, 0); if (ArrayCount1 - ArgVal3 < ArrayCount4) MinCount = (UWORD)(ArrayCount1 - ArgVal3); else MinCount = ArrayCount4; for (i = 0; i < MinCount; i++) { Status = cCmdInterpPolyUnop2(OP_MOV, INC_ID(Arg1), ARRAY_ELEM_OFFSET(DVIndex1, ArgVal3 + i), INC_ID(Arg4), ARRAY_ELEM_OFFSET(DVIndex4, i)); if (IS_ERR(Status)) return Status; } } } break; case OP_ARRSUBSET: { //Arg1 - Dst //Arg2 - Src //Arg3 - Index //Arg4 - Length Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; Arg4 = pCode[4]; NXT_ASSERT(cCmdDSType(Arg1) == TC_ARRAY); NXT_ASSERT(cCmdDSType(Arg2) == TC_ARRAY); ArrayCount2 = cCmdArrayCount(Arg2, 0); if (Arg3 != NOT_A_DS_ID) ArgVal3 = cCmdGetScalarValFromDataArg(Arg3, 0); else //Index input unwired ArgVal3 = 0; if (Arg4 != NOT_A_DS_ID) ArgVal4 = cCmdGetScalarValFromDataArg(Arg4, 0); else //Length input unwired, set to "rest" ArgVal4 = (UWORD)(ArrayCount2 - ArgVal3); //Bounds check if (ArgVal3 > ArrayCount2) { //Illegal range - return empty subset Status = cCmdDSArrayAlloc(Arg1, 0, 0); return Status; } //Set MinCount to "rest" MinCount = (UWORD)(ArrayCount2 - ArgVal3); // Copy "Length" if it is less than "rest" if (ArgVal4 < (ULONG)MinCount) MinCount = (UWORD)ArgVal4; //Allocate Dst array Status = cCmdDSArrayAlloc(Arg1, 0, MinCount); if (IS_ERR(Status)) return Status; DVIndex1 = cCmdGetDVIndex(Arg1, 0); DVIndex2 = cCmdGetDVIndex(Arg2, 0); //Move src subset to dst for (i = 0; i < MinCount; i++) { Status = cCmdInterpPolyUnop2(OP_MOV, INC_ID(Arg1), ARRAY_ELEM_OFFSET(DVIndex1, i), INC_ID(Arg2), ARRAY_ELEM_OFFSET(DVIndex2, ArgVal3 + i)); if (IS_ERR(Status)) return Status; } } break; case OP_STRSUBSET: { //Arg1 - Dst //Arg2 - Src //Arg3 - Index //Arg4 - Length Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; Arg4 = pCode[4]; NXT_ASSERT(cCmdDSType(Arg1) == TC_ARRAY); NXT_ASSERT(cCmdDSType(INC_ID(Arg1)) == TC_UBYTE); NXT_ASSERT(cCmdDSType(Arg2) == TC_ARRAY); NXT_ASSERT(cCmdDSType(INC_ID(Arg2)) == TC_UBYTE); ArrayCount2 = cCmdArrayCount(Arg2, 0); //Remove NULL from Count ArrayCount2--; if (Arg3 != NOT_A_DS_ID) ArgVal3 = cCmdGetScalarValFromDataArg(Arg3, 0); else //Index input unwired ArgVal3 = 0; if (Arg4 != NOT_A_DS_ID) ArgVal4 = cCmdGetScalarValFromDataArg(Arg4, 0); else //Length input unwired, set to "rest" ArgVal4 = (UWORD)(ArrayCount2 - ArgVal3); //Bounds check if (ArgVal3 > ArrayCount2) { //Illegal range - return empty string Status = cCmdDSArrayAlloc(Arg1, 0, 1); if (!IS_ERR(Status)) { pArg1 = cCmdResolveDataArg(Arg1, 0, NULL); *((UBYTE *)pArg1) = '\0'; } return Status; } //Set MinCount to "rest" MinCount = (UWORD)(ArrayCount2 - ArgVal3); // Copy "Length" if it is less than "rest" if (ArgVal4 < (ArrayCount2 - ArgVal3)) MinCount = (UWORD)ArgVal4; //Allocate Dst array Status = cCmdDSArrayAlloc(Arg1, 0, (UWORD)(MinCount + 1)); if (IS_ERR(Status)) return Status; pArg1 = cCmdResolveDataArg(Arg1, 0, NULL); pArg2 = cCmdResolveDataArg(Arg2, 0, NULL); //Move src subset to dst memmove((UBYTE *)pArg1, (UBYTE *)pArg2 + ArgVal3, MinCount); //Append NULL terminator to Dst *((UBYTE *)pArg1 + MinCount) = '\0'; } break; case OP_SETOUT: { Status = cCmdExecuteSetOut(pCode); } break; case OP_ARRBUILD: { // Arg1 - Instruction Size in bytes // Arg2 - Dst // Arg3-N - Srcs Arg2 = pCode[2]; NXT_ASSERT(cCmdDSType(Arg2) == TC_ARRAY); //Number of Srcs = total code words - 3 (account for opcode word, size, and Dst) //!!! Argument access like this is potentially unsafe. //A function/macro which checks proper encoding would be better SrcCount = (pCode[1] / 2) - 3; //Calculate Dst array count ArrayCount2 = 0; for (i = 0; i < SrcCount; i++) { TmpDSID = pCode[3 + i]; NXT_ASSERT(cCmdIsDSElementIDSane(TmpDSID)); //If the type descriptors are the same, then the input is an array, not a single element if (cCmdCompareDSType(Arg2, TmpDSID)) { NXT_ASSERT(cCmdDSType(TmpDSID) == TC_ARRAY); ArrayCount2 += cCmdArrayCount(TmpDSID, 0); } else { //Assert that the output is an array of this input type NXT_ASSERT(cCmdCompareDSType(INC_ID(Arg2), TmpDSID)); ArrayCount2++; } } //Allocate Dst array Status = cCmdDSArrayAlloc(Arg2, 0, ArrayCount2); if (IS_ERR(Status)) return Status; DVIndex2 = cCmdGetDVIndex(Arg2, 0); //Move Src(s) to Dst DstIndex = 0; for (i = 0; i < SrcCount; i++) { TmpDSID = pCode[3 + i]; //If the type descriptors are the same, then the input is an array, not a single element if (cCmdCompareDSType(Arg2, TmpDSID)) { NXT_ASSERT(cCmdDSType(TmpDSID) == TC_ARRAY); TmpDVIndex = cCmdGetDVIndex(TmpDSID, 0); // if flat, use memmove, otherwise this stuff if(cCmdDSType(INC_ID(TmpDSID)) <= TC_LAST_INT_SCALAR) { memmove(VarsCmd.pDataspace + ARRAY_ELEM_OFFSET(DVIndex2, DstIndex), VarsCmd.pDataspace + DV_ARRAY[TmpDVIndex].Offset, (UWORD)(DV_ARRAY[TmpDVIndex].ElemSize * DV_ARRAY[TmpDVIndex].Count)); DstIndex += DV_ARRAY[TmpDVIndex].Count; } else for (j = 0; j < DV_ARRAY[TmpDVIndex].Count; j++) { Status = cCmdInterpPolyUnop2(OP_MOV, INC_ID(Arg2), ARRAY_ELEM_OFFSET(DVIndex2, DstIndex), INC_ID(TmpDSID), ARRAY_ELEM_OFFSET(TmpDVIndex, j)); if (IS_ERR(Status)) return Status; DstIndex++; } } else { //Assert that the output is an array of this input type NXT_ASSERT(cCmdCompareDSType(INC_ID(Arg2), TmpDSID)); Status = cCmdInterpPolyUnop2(OP_MOV, INC_ID(Arg2), ARRAY_ELEM_OFFSET(DVIndex2, DstIndex), TmpDSID, 0); if (IS_ERR(Status)) return Status; DstIndex++; } } NXT_ASSERT(DstIndex == ArrayCount2); } break; case OP_STRCAT: { // Arg1 - Instruction Size in bytes // Arg2 - Dst // Arg3-N - Srcs Arg2 = pCode[2]; //Make sure Dst arg is a string NXT_ASSERT(cCmdDSType(Arg2) == TC_ARRAY); NXT_ASSERT(cCmdDSType(INC_ID(Arg2)) == TC_UBYTE); //Number of Srcs = total code words - 3 (account for opcode word, size, and Dst) //!!! Argument access like this is potentially unsafe. //A function/macro which checks proper encoding would be better SrcCount = (pCode[1] / 2) - 3; //Calculate Dst array count ArrayCount2 = 0; for (i = 0; i < SrcCount; i++) { TmpDSID = pCode[3 + i]; NXT_ASSERT(cCmdIsDSElementIDSane(TmpDSID)); //Make sure Src arg is a string //!!! Type checks here should be richer to allow array of strings as input (match LabVIEW behavior) NXT_ASSERT(cCmdDSType(TmpDSID) == TC_ARRAY); if (cCmdDSType(INC_ID(TmpDSID)) != TC_UBYTE) { NXT_BREAK; return ERR_ARG; } ArrayCount3 = cCmdArrayCount(TmpDSID, 0); NXT_ASSERT(ArrayCount3 > 0); //Subtract NULL terminator from Src array count ArrayCount3--; //Increase Dst array count by Src array count ArrayCount2 += ArrayCount3; } //Add room for NULL terminator ArrayCount2++; //Allocate Dst array Status = cCmdDSArrayAlloc(Arg2, 0, ArrayCount2); if (IS_ERR(Status)) return Status; //Move Src(s) to Dst DstIndex = 0; pArg2 = cCmdResolveDataArg(Arg2, 0, NULL); for (i = 0; i < SrcCount; i++) { TmpDSID = pCode[3 + i]; pArg3 = cCmdResolveDataArg(TmpDSID, 0, NULL); ArrayCount3 = cCmdArrayCount(TmpDSID, 0); NXT_ASSERT(ArrayCount3 > 0); //Subtract NULL terminator from Src array count ArrayCount3--; memmove((UBYTE *)pArg2 + DstIndex, pArg3, ArrayCount3); DstIndex += ArrayCount3; } //Append NULL terminator to Dst *((UBYTE *)pArg2 + DstIndex) = '\0'; DstIndex++; NXT_ASSERT(DstIndex == ArrayCount2); } break; case OP_UNFLATTEN: { //Arg1 - Dst //Arg2 - Err (output) //Arg3 - Src (byte stream) //Arg4 - Type //The Type arg is a preallocated structure of the exact size you //want to unflatten into. This allows us to support unflattening arbitrary types. //!!! Currently, both outputs must have valid destinations. // It would be trivial to handle NOT_A_DS_ID to avoid dummy // allocations when outputs are unused. Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; Arg4 = pCode[4]; //Move Type template to Dst //This provides a default value for Dst and makes sure Dst is properly sized Status= cCmdMove(Arg1, Arg4); if (IS_ERR(Status)) return Status; //Resolve error data pointer pArg2 = cCmdResolveDataArg(Arg2, 0, &TypeCode2); //Make sure Arg3 is a String NXT_ASSERT(cCmdDSType(Arg3) == TC_ARRAY); NXT_ASSERT(cCmdDSType(INC_ID(Arg3)) == TC_UBYTE); ArrayCount3 = cCmdArrayCount(Arg3, 0); //Take NULL terminator out of count ArrayCount3--; Size = cCmdCalcFlattenedSize(Arg4, 0); //Check that we have a proper type template to unflatten into if (ArrayCount3 == Size) { pArg3 = cCmdResolveDataArg(Arg3, 0, NULL); Offset = 0; Status = cCmdUnflattenFromByteArray(pArg3, &Offset, Arg1, 0); //!!! Status ignored from cCmdUnflattenFromByteArray // If future revisions of this function provide better error checking, // Err arg should be conditionally set based on the result. //Unflatten succeeded; set Err arg to FALSE cCmdSetVal(pArg2, TypeCode2, FALSE); NXT_ASSERT(Offset == Size); } else { //Unflatten failed; set Err arg to TRUE cCmdSetVal(pArg2, TypeCode2, TRUE); } } break; case OP_STRINGTONUM: { float ArgValF; SLONG decimals= 0; UBYTE cont= TRUE; // Arg1 - Dst number (output) // Arg2 - Offset past match (output) // Arg3 - Src string // Arg4 - Offset // Arg5 - Default (type/value) //!!! Currently, both outputs must have valid destinations. // It would be trivial to handle NOT_A_DS_ID to avoid dummy // allocations when outputs are unused. Arg1 = pCode[1]; Arg2 = pCode[2]; Arg3 = pCode[3]; Arg4 = pCode[4]; Arg5 = pCode[5]; pArg1 = cCmdResolveDataArg(Arg1, 0, &TypeCode1); pArg3 = cCmdResolveDataArg(Arg3, 0, &TypeCode3); if (Arg4 != NOT_A_DS_ID) ArgVal4 = cCmdGetScalarValFromDataArg(Arg4, 0); else //Offset input unwired ArgVal4 = 0; if (Arg5 != NOT_A_DS_ID) { pArg5 = cCmdResolveDataArg(Arg5, 0, &TypeCode5); ArgVal5 = cCmdGetVal(pArg5, TypeCode5); } else //Default input unwired { ArgVal5 = 0; } //Read number from string if (sscanf(((PSZ)pArg3 + ArgVal4), "%f", &ArgValF) == 1) { i = (UWORD)ArgVal4; //Scan until we see the number, consumes negative sign too while ((((UBYTE *)pArg3)[i] < '0') || (((UBYTE *)pArg3)[i] > '9')) i++; //Scan until we get past the number and no more than one decimal while (cont) { if ((((UBYTE *)pArg3)[i] >= '0') && (((UBYTE *)pArg3)[i] <= '9')) i++; else if(((UBYTE *)pArg3)[i] == '.' && !decimals) { i++; decimals++; } else cont= FALSE; } ArgVal2 = i; } else { //Number wasn't found in string, use defaults ArgValF = ArgVal5; ArgVal2 = 0; } //Set outputs cCmdSetValFlt(pArg1, TypeCode1, ArgValF); cCmdSetScalarValFromDataArg(Arg2, ArgVal2); } break; default: { //Fatal error: Unrecognized instruction NXT_BREAK; Status = ERR_INSTR; } break; } return (Status); } // //Support functions for lowspeed (I2C devices, i.e. ultrasonic sensor) communications // //Simple lookup table for pMapLowSpeed->ChannelState[Port] values //This is used to keep VM status code handling consistent //...and ChannelState gives us too much information, anyway... static const NXT_STATUS MapLStoVMStat[6] = { NO_ERR, //LOWSPEED_IDLE, STAT_COMM_PENDING, //LOWSPEED_INIT, STAT_COMM_PENDING, //LOWSPEED_LOAD_BUFFER, STAT_COMM_PENDING, //LOWSPEED_COMMUNICATING, ERR_COMM_BUS_ERR, //LOWSPEED_ERROR, STAT_COMM_PENDING, //LOWSPEED_DONE (really means c_lowspeed state machine is resetting) }; //cCmdLSCheckStatus //Check lowspeed port status, optionally returning bytes available in the buffer for reading NXT_STATUS cCmdLSCheckStatus(UBYTE Port) { if (Port >= NO_OF_LOWSPEED_COM_CHANNEL) { return (ERR_COMM_CHAN_INVALID); } INPUTSTRUCT * pInput = &(pMapInput->Inputs[Port]); //If port is not configured properly ahead of time, report that error //!!! This seems like the right policy, but may restrict otherwise valid read operations... if (!(pInput->SensorType == LOWSPEED_9V || pInput->SensorType == LOWSPEED) || !(pInput->InvalidData == FALSE)) { return (ERR_COMM_CHAN_NOT_READY); } return (MapLStoVMStat[pMapLowSpeed->ChannelState[Port]]); } //cCmdLSCalcBytesReady //Calculate true number of bytes available in the inbound LS buffer UBYTE cCmdLSCalcBytesReady(UBYTE Port) { SLONG Tmp; //Expect callers to validate Port, but short circuit here to be safe. if (Port >= NO_OF_LOWSPEED_COM_CHANNEL) return 0; LSBUF * pInBuf = &(pMapLowSpeed->InBuf[Port]); //Normally, bytes available is a simple difference. Tmp = pInBuf->InPtr - pInBuf->OutPtr; //If InPtr is actually behind OutPtr, circular buffer has wrapped. Account for wrappage... if (Tmp < 0) Tmp = (pInBuf->InPtr + (SIZE_OF_LSBUF - pInBuf->OutPtr)); return (UBYTE)(Tmp); } //cCmdLSWrite //Write BufLength bytes into specified port's lowspeed buffer and kick off comm process to device NXT_STATUS cCmdLSWrite(UBYTE Port, UBYTE BufLength, UBYTE *pBuf, UBYTE ResponseLength) { if (Port >= NO_OF_LOWSPEED_COM_CHANNEL) { return (ERR_COMM_CHAN_INVALID); } if (BufLength > SIZE_OF_LSBUF || ResponseLength > SIZE_OF_LSBUF) { return (ERR_INVALID_SIZE); } INPUTSTRUCT * pInput = &(pMapInput->Inputs[Port]); UBYTE * pChState = &(pMapLowSpeed->ChannelState[Port]); LSBUF * pOutBuf = &(pMapLowSpeed->OutBuf[Port]); //Only start writing process if port is properly configured and c_lowspeed module is ready if ((pInput->SensorType == LOWSPEED_9V || pInput->SensorType == LOWSPEED) && (pInput->InvalidData == FALSE) && (*pChState == LOWSPEED_IDLE) || (*pChState == LOWSPEED_ERROR)) { pOutBuf->InPtr = 0; pOutBuf->OutPtr = 0; memcpy(pOutBuf->Buf, pBuf, BufLength); pOutBuf->InPtr = (UBYTE)BufLength; pMapLowSpeed->InBuf[Port].BytesToRx = ResponseLength; *pChState = LOWSPEED_INIT; pMapLowSpeed->State |= (COM_CHANNEL_ONE_ACTIVE << Port); return (NO_ERR); } else { //!!! Would be more consistent to return STAT_COMM_PENDING if c_lowspeed is busy return (ERR_COMM_CHAN_NOT_READY); } } //cCmdLSRead //Read BufLength bytes from specified port's lowspeed buffer NXT_STATUS cCmdLSRead(UBYTE Port, UBYTE BufLength, UBYTE * pBuf) { UBYTE BytesReady, BytesToRead; if (Port >= NO_OF_LOWSPEED_COM_CHANNEL) { return (ERR_COMM_CHAN_INVALID); } if (BufLength > SIZE_OF_LSBUF) { return (ERR_INVALID_SIZE); } BytesReady = cCmdLSCalcBytesReady(Port); if (BufLength > BytesReady) { return (ERR_COMM_CHAN_NOT_READY); } BytesToRead = BufLength; LSBUF * pInBuf = &(pMapLowSpeed->InBuf[Port]); //If the bytes we want to read wrap around the end, we must first read the end, then reset back to the beginning if (pInBuf->OutPtr + BytesToRead >= SIZE_OF_LSBUF) { BytesToRead = SIZE_OF_LSBUF - pInBuf->OutPtr; memcpy(pBuf, pInBuf->Buf + pInBuf->OutPtr, BytesToRead); pInBuf->OutPtr = 0; pBuf += BytesToRead; BytesToRead = BufLength - BytesToRead; } memcpy(pBuf, pInBuf->Buf + pInBuf->OutPtr, BytesToRead); pInBuf->OutPtr += BytesToRead; return (NO_ERR); } // //Wrappers for OP_SYSCALL // // //cCmdWrapFileOpenRead //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: File Handle, U8 return //ArgV[2]: Filename, CStr //ArgV[3]: Length, U32 return NXT_STATUS cCmdWrapFileOpenRead(UBYTE * ArgV[]) { LOADER_STATUS LStatus; DV_INDEX DVIndex; //Resolve array argument DVIndex = *(DV_INDEX *)(ArgV[2]); ArgV[2] = cCmdDVPtr(DVIndex); LStatus = pMapLoader->pFunc(OPENREAD, ArgV[2], NULL, (ULONG *)ArgV[3]); //Add entry into FileHandleTable if (LOADER_ERR(LStatus) == SUCCESS) { VarsCmd.FileHandleTable[LOADER_HANDLE(LStatus)][0] = 'r'; strcpy((PSZ)(VarsCmd.FileHandleTable[LOADER_HANDLE(LStatus)] + 1), (PSZ)(ArgV[2])); } //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); //File handle in low byte of LStatus *(ArgV[1]) = LOADER_HANDLE(LStatus); return NO_ERR; } //cCmdWrapFileOpenWrite //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: File Handle, U8 return //ArgV[2]: Filename, CStr //ArgV[3]: Length, U32 return NXT_STATUS cCmdWrapFileOpenWrite(UBYTE * ArgV[]) { LOADER_STATUS LStatus; DV_INDEX DVIndex; //Resolve array argument DVIndex = *(DV_INDEX *)(ArgV[2]); ArgV[2] = cCmdDVPtr(DVIndex); LStatus = pMapLoader->pFunc(OPENWRITEDATA, ArgV[2], NULL, (ULONG *)ArgV[3]); //Add entry into FileHandleTable if (LOADER_ERR(LStatus) == SUCCESS) { VarsCmd.FileHandleTable[LOADER_HANDLE(LStatus)][0] = 'w'; strcpy((PSZ)(VarsCmd.FileHandleTable[LOADER_HANDLE(LStatus)] + 1), (PSZ)(ArgV[2])); } //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); //File handle in low byte of LStatus *(ArgV[1]) = LOADER_HANDLE(LStatus); return NO_ERR; } //cCmdWrapFileOpenAppend //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: File Handle, U8 return //ArgV[2]: Filename, CStr //ArgV[3]: Length Remaining, U32 return NXT_STATUS cCmdWrapFileOpenAppend(UBYTE * ArgV[]) { LOADER_STATUS LStatus; DV_INDEX DVIndex; //Resolve array argument DVIndex = *(DV_INDEX *)(ArgV[2]); ArgV[2] = cCmdDVPtr(DVIndex); LStatus = pMapLoader->pFunc(OPENAPPENDDATA, ArgV[2], NULL, (ULONG *)ArgV[3]); //Add entry into FileHandleTable if (LOADER_ERR(LStatus) == SUCCESS) { VarsCmd.FileHandleTable[LOADER_HANDLE(LStatus)][0] = 'w'; strcpy((PSZ)(VarsCmd.FileHandleTable[LOADER_HANDLE(LStatus)] + 1), (PSZ)(ArgV[2])); } //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); //File handle in low byte of LStatus *(ArgV[1]) = LOADER_HANDLE(LStatus); return NO_ERR; } //cCmdWrapFileRead //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: File Handle, U8 in/out //ArgV[2]: Buffer, CStr out //ArgV[3]: Length, U32 in/out NXT_STATUS cCmdWrapFileRead(UBYTE * ArgV[]) { NXT_STATUS Status = NO_ERR; LOADER_STATUS LStatus; DV_INDEX DVIndex; //Resolve array argument DVIndex = *(DV_INDEX *)(ArgV[2]); //Size Buffer to Length //Add room for null terminator to length Status = cCmdDVArrayAlloc(DVIndex, (UWORD)(*(ULONG *)ArgV[3] + 1)); if (IS_ERR(Status)) return Status; ArgV[2] = cCmdDVPtr(DVIndex); LStatus = pMapLoader->pFunc(READ, ArgV[1], ArgV[2], (ULONG *)ArgV[3]); //Tack on NULL terminator //Note that loader code may have adjusted length (*ArgV[3]) if all requested data was not available //!!! Better solution would be to resize buffer to new length + 1, // but then you must also be wary of side effects if resize allocation fails! *(ArgV[2] + *(ULONG *)ArgV[3]) = '\0'; //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); //File handle in low byte of LStatus *(ArgV[1]) = LOADER_HANDLE(LStatus); return Status; } //cCmdWrapFileWrite //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: File Handle, U8 in/out //ArgV[2]: Buffer, CStr //ArgV[3]: Length, U32 return NXT_STATUS cCmdWrapFileWrite(UBYTE * ArgV[]) { LOADER_STATUS LStatus; DV_INDEX DVIndex; //Resolve array argument DVIndex = *(DV_INDEX *)(ArgV[2]); ArgV[2] = cCmdDVPtr(DVIndex); LStatus = pMapLoader->pFunc(WRITE, ArgV[1], ArgV[2], (ULONG *)ArgV[3]); //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); //File handle in low byte of LStatus *(ArgV[1]) = LOADER_HANDLE(LStatus); return NO_ERR; } //cCmdWrapFileClose //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: File Handle, U8 NXT_STATUS cCmdWrapFileClose(UBYTE * ArgV[]) { LOADER_STATUS LStatus; //!!! This bounds check also exists in dLoaderCloseHandle(), but we provide an explicit error code if (*(ArgV[1]) >= MAX_HANDLES) { *((UWORD *)ArgV[0]) = ILLEGALHANDLE; return NO_ERR; } LStatus = pMapLoader->pFunc(CLOSE, ArgV[1], NULL, NULL); //Clear entry in FileHandleTable memset(VarsCmd.FileHandleTable[*(ArgV[1])], 0, FILENAME_LENGTH + 2); //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); return NO_ERR; } //cCmdWrapFileResolveHandle //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: File Handle, U8 return //ArgV[2]: Write Handle?, Bool return //ArgV[3]: Filename, CStr NXT_STATUS cCmdWrapFileResolveHandle (UBYTE * ArgV[]) { UBYTE i; DV_INDEX DVIndex; //Resolve array argument DVIndex = *(DV_INDEX *)(ArgV[3]); ArgV[3] = cCmdDVPtr(DVIndex); for (i = 0; i < MAX_HANDLES; i++) { if (strcmp((PSZ)(ArgV[3]), (PSZ)(VarsCmd.FileHandleTable[i] + 1)) == 0) { *(ArgV[2]) = (VarsCmd.FileHandleTable[i][0] == 'w'); break; } } if (i == MAX_HANDLES) { i = NOT_A_HANDLE; *((UWORD *)ArgV[0]) = HANDLEALREADYCLOSED; } else { *((UWORD *)ArgV[0]) = SUCCESS; } *(ArgV[1]) = i; return NO_ERR; } //cCmdWrapFileRename //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: Old Filename, CStr //ArgV[2]: New Filename, CStr NXT_STATUS cCmdWrapFileRename (UBYTE * ArgV[]) { LOADER_STATUS LStatus; ULONG Tmp; DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[1]); ArgV[1] = cCmdDVPtr(DVIndex); DVIndex = *(DV_INDEX *)(ArgV[2]); ArgV[2] = cCmdDVPtr(DVIndex); //!!! Tmp placeholder passed into loader code to avoid illegal dereferencing. LStatus = pMapLoader->pFunc(RENAMEFILE, ArgV[1], ArgV[2], &Tmp); //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); return NO_ERR; } //cCmdWrapFileDelete //ArgV[0]: (Function return) Loader status, U16 return //ArgV[1]: Filename, CStr NXT_STATUS cCmdWrapFileDelete (UBYTE * ArgV[]) { LOADER_STATUS LStatus; DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[1]); ArgV[1] = cCmdDVPtr(DVIndex); LStatus = pMapLoader->pFunc(DELETE, ArgV[1], NULL, NULL); //Status code in high byte of LStatus *((UWORD *)ArgV[0]) = LOADER_ERR(LStatus); return NO_ERR; } // //cCmdWrapSoundPlayFile //ArgV[0]: (Return value) Status code, SBYTE //ArgV[1]: Filename, CStr //ArgV[2]: Loop?, UBYTE (bool) //ArgV[3]: Volume, UBYTE // NXT_STATUS cCmdWrapSoundPlayFile(UBYTE * ArgV[]) { DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[1]); UBYTE sndVol= *(ArgV[3]); ArgV[1] = cCmdDVPtr(DVIndex); //!!! Should check filename and/or existence and return error before proceeding strncpy((PSZ)(pMapSound->SoundFilename), (PSZ)(ArgV[1]), FILENAME_LENGTH); if (*(ArgV[2]) == TRUE) pMapSound->Mode = SOUND_LOOP; else pMapSound->Mode = SOUND_ONCE; if(sndVol > 4) sndVol= 4; pMapSound->Volume = sndVol; //SampleRate of '0' means "let file specify SampleRate" pMapSound->SampleRate = 0; pMapSound->Flags |= SOUND_UPDATE; *((SBYTE*)(ArgV[0])) = (NO_ERR); return (NO_ERR); } // //cCmdWrapSoundPlayTone //ArgV[0]: (Return value) Status code, SBYTE //ArgV[1]: Frequency, UWORD //ArgV[2]: Duration, UWORD //ArgV[3]: Loop?, UBYTE (Boolean) //ArgV[4]: Volume, UBYTE // NXT_STATUS cCmdWrapSoundPlayTone(UBYTE * ArgV[]) { UBYTE sndVol= *(ArgV[4]); pMapSound->Freq = *(UWORD*)(ArgV[1]); pMapSound->Duration = *(UWORD*)(ArgV[2]); if(sndVol > 4) sndVol= 4; pMapSound->Volume = sndVol; pMapSound->Flags |= SOUND_UPDATE; if (*(ArgV[3]) == TRUE) pMapSound->Mode = SOUND_TONE | SOUND_LOOP; else pMapSound->Mode = SOUND_TONE; *((SBYTE*)(ArgV[0])) = (NO_ERR); return (NO_ERR); } // //cCmdWrapSoundGetState //ArgV[0]: (Return value) sound module state, UBYTE //ArgV[1]: Flags, UBYTE // NXT_STATUS cCmdWrapSoundGetState(UBYTE * ArgV[]) { *(ArgV[0]) = pMapSound->State; *(ArgV[1]) = pMapSound->Flags; return (NO_ERR); } // //cCmdWrapSoundSetState //ArgV[0]: (Return value) sound module state, UBYTE //ArgV[1]: State, UBYTE //ArgV[2]: Flags, UBYTE // NXT_STATUS cCmdWrapSoundSetState(UBYTE * ArgV[]) { pMapSound->State = *(ArgV[1]); //Return same state we just set, mostly for interface consistency *(ArgV[0]) = pMapSound->State; //OR in provided flags (usually 0) pMapSound->Flags |= *(ArgV[2]); return (NO_ERR); } // //cCmdWrapReadButton //ArgV[0]: (Function return) Status code, SBYTE //ArgV[1]: Index (U8) //ArgV[2]: Pressed (bool) //ArgV[3]: Count (U8) (count of press-then-release cycles) //ArgV[4]: ResetCount? (bool in) // NXT_STATUS cCmdWrapReadButton(UBYTE * ArgV[]) { UBYTE btnIndex; btnIndex = *((UBYTE*)(ArgV[1])); if (btnIndex < NO_OF_BTNS) { //Set pressed boolean output if (pMapButton->State[btnIndex] & PRESSED_STATE) *(ArgV[2]) = TRUE; else *(ArgV[2]) = FALSE; //Set count output *(ArgV[3]) = (UBYTE)(pMapButton->BtnCnt[btnIndex].RelCnt); //Optionally reset internal count if (*(ArgV[4]) != 0) { pMapButton->BtnCnt[btnIndex].RelCnt = 0; //Need to clear short and long counts too, because RelCnt depends on them. No known side effects. pMapButton->BtnCnt[btnIndex].ShortRelCnt = 0; pMapButton->BtnCnt[btnIndex].LongRelCnt = 0; } // Set status code 'OK' *((SBYTE*)(ArgV[0])) = NO_ERR; } else { //Bad button index specified, return error and default outputs *((SBYTE*)(ArgV[0])) = ERR_INVALID_PORT; *(ArgV[2]) = FALSE; *(ArgV[3]) = 0; } return (NO_ERR); } // //cCmdWrapCommLSWrite //ArgV[0]: (return) Status code, SBYTE //ArgV[1]: Port specifier, UBYTE //ArgV[2]: Buffer to send, UBYTE array, only SIZE_OF_LSBUF bytes will be used //ArgV[3]: ResponseLength, UBYTE, specifies expected bytes back from slave device // NXT_STATUS cCmdWrapCommLSWrite(UBYTE * ArgV[]) { SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UBYTE Port = *(ArgV[1]); UBYTE * pBuf; UWORD BufLength; UBYTE ResponseLength = *(ArgV[3]); DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[2]); pBuf = cCmdDVPtr(DVIndex); BufLength = DV_ARRAY[DVIndex].Count; *pReturnVal = cCmdLSWrite(Port, (UBYTE)BufLength, pBuf, ResponseLength); return (NO_ERR); } // //cCmdWrapCommLSCheckStatus //ArgV[0]: (return) Status code, SBYTE //ArgV[1]: Port specifier, UBYTE //ArgV[2]: BytesReady, UBYTE // NXT_STATUS cCmdWrapCommLSCheckStatus(UBYTE * ArgV[]) { UBYTE Port = *(ArgV[1]); *((SBYTE*)(ArgV[0])) = cCmdLSCheckStatus(Port); *((UBYTE*)(ArgV[2])) = cCmdLSCalcBytesReady(Port); return (NO_ERR); } // //cCmdWrapCommLSRead //ArgV[0]: (return) Status code, SBYTE //ArgV[1]: Port specifier, UBYTE //ArgV[2]: Buffer for data, UBYTE array, max SIZE_OF_LSBUF bytes will be written //ArgV[3]: BufferLength, UBYTE, specifies size of buffer requested // NXT_STATUS cCmdWrapCommLSRead(UBYTE * ArgV[]) { SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UBYTE Port = *(ArgV[1]); UBYTE * pBuf; UBYTE BufLength = *(ArgV[3]); UBYTE BytesToRead; DV_INDEX DVIndex = *(DV_INDEX *)(ArgV[2]); NXT_STATUS AllocStatus; *pReturnVal = cCmdLSCheckStatus(Port); BytesToRead = cCmdLSCalcBytesReady(Port); //If channel is OK and has data ready for us, put the data into outgoing buffer if (!IS_ERR(*pReturnVal) && BytesToRead > 0) { //Limit buffer to available data if (BufLength > BytesToRead) BufLength = BytesToRead; AllocStatus = cCmdDVArrayAlloc(DVIndex, BufLength); if (IS_ERR(AllocStatus)) return (AllocStatus); pBuf = cCmdDVPtr(DVIndex); *pReturnVal = cCmdLSRead(Port, BufLength, pBuf); } //Else, the channel has an error and/or there's no data to read; clear the output array else { AllocStatus = cCmdDVArrayAlloc(DVIndex, 0); if (IS_ERR(AllocStatus)) return (AllocStatus); } return (NO_ERR); } // //cCmdWrapRandomNumber //ArgV[0]: (return) Random number, SWORD // NXT_STATUS cCmdWrapRandomNumber(UBYTE * ArgV[]) { static UBYTE count = 0; SWORD random; if (count == 0) srand(dTimerRead()); if (count > 20) count = 0; else count++; //!!! IAR's implementation of the rand() library function returns signed values, and we want it that way. //Some stdlib implementations may return only positive numbers, so be wary if this code is ported. random = rand(); *((SWORD *)ArgV[0]) = random; return NO_ERR; } // //cCmdWrapGetStartTick //ArgV[0]: (return) Start Tick, ULONG // NXT_STATUS cCmdWrapGetStartTick(UBYTE * ArgV[]) { *((ULONG *)ArgV[0]) = VarsCmd.StartTick; return NO_ERR; } // //cCmdWrapMessageWrite //ArgV[0]: (return) Error Code, SBYTE (NXT_STATUS) //ArgV[1]: QueueID, UBYTE //ArgV[2]: Message, CStr // NXT_STATUS cCmdWrapMessageWrite(UBYTE * ArgV[]) { NXT_STATUS Status = NO_ERR; DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[2]); ArgV[2] = cCmdDVPtr(DVIndex); Status = cCmdMessageWrite(*(UBYTE *)(ArgV[1]), ArgV[2], DV_ARRAY[DVIndex].Count); *(SBYTE *)(ArgV[0]) = Status; if (IS_FATAL(Status)) return Status; else return (NO_ERR); } // //cCmdWrapColorSensorRead //ArgV[0]: (return) Error code, SBYTE //ArgV[1]: Port, UBYTE //ArgV[2]: SensorValue, SWORD //ArgV[3]: RawArray, UWORD[NO_OF_COLORS] //ArgV[4]: NormalizedArray, UWORD[NO_OF_COLORS] //ArgV[5]: ScaledArray, SWORD[NO_OF_COLORS] //ArgV[6]: InvalidData, UBYTE // NXT_STATUS cCmdWrapColorSensorRead (UBYTE * ArgV[]) { DV_INDEX DVIndex; NXT_STATUS Status = NO_ERR; //Resolve return val arguments SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); //Resolve Port argument UBYTE Port = *(UBYTE*)(ArgV[1]); //Resolve SensorValue SWORD SensorValue = *(SWORD*)(ArgV[2]); //Resolve RawArray as array DVIndex = *(DV_INDEX*)(ArgV[3]); NXT_ASSERT(IS_DV_INDEX_SANE(DestDVIndex)); Status= cCmdDVArrayAlloc(DVIndex, NO_OF_COLORS); if (IS_ERR(Status)) return (Status); ArgV[3] = cCmdDVPtr (DVIndex); //Resolve NormalizedArray as array DVIndex = *(DV_INDEX*)(ArgV[4]); NXT_ASSERT(IS_DV_INDEX_SANE(DestDVIndex)); Status= cCmdDVArrayAlloc(DVIndex, NO_OF_COLORS); if (IS_ERR(Status)) return (Status); ArgV[4] = cCmdDVPtr (DVIndex); //Resolve ScaledArray as array DVIndex = *(DV_INDEX*)(ArgV[5]); NXT_ASSERT(IS_DV_INDEX_SANE(DestDVIndex)); Status= cCmdDVArrayAlloc(DVIndex, NO_OF_COLORS); if (IS_ERR(Status)) return (Status); ArgV[5] = cCmdDVPtr (DVIndex); //Resolve InvalidData UBYTE InvalidData = *(UBYTE*)(ArgV[6]); //call implementation with unwrapped parameters *pReturnVal = cCmdColorSensorRead (Port, &SensorValue, (UWORD*)ArgV[3], (UWORD*)ArgV[4], (SWORD*)ArgV[5], &InvalidData); *(ArgV[2]) = SensorValue; *(ArgV[6]) = InvalidData; if (IS_ERR(*pReturnVal)){ return (*pReturnVal); } return NO_ERR; } #define UNPACK_STATUS(StatusWord) ((SBYTE)(StatusWord)) NXT_STATUS cCmdBTCheckStatus(UBYTE Connection) { //If specified connection is invalid, return error code to the user. if (Connection >= SIZE_OF_BT_CONNECT_TABLE) { return (ERR_INVALID_PORT); } //INPROGRESS means a request is currently pending completion by the comm module if (VarsCmd.CommStat == INPROGRESS) { return (STAT_COMM_PENDING); } //Translate BTBUSY to ERR_COMM_CHAN_NOT_READY //And check if specified connection is indeed configured else if (VarsCmd.CommStat == (SWORD)BTBUSY || (pMapComm->BtConnectTable[Connection].Name[0]) == '\0') { return (ERR_COMM_CHAN_NOT_READY); } else { return (UNPACK_STATUS(VarsCmd.CommStat)); } } //Default packet to send for a remote MESSAGE_READ command. //3rd byte must be replaced with remote mailbox (QueueID) //4th byte must be replaced with local mailbox static UBYTE RemoteMsgReadPacket[5] = {0x00, 0x13, 0xFF, 0xFF, 0x01}; // //cCmdWrapMessageRead //ArgV[0]: (return) Error Code, SBYTE (NXT_STATUS) //ArgV[1]: QueueID, UBYTE //ArgV[2]: Remove, UBYTE //ArgV[3]: (return) Message, CStr // NXT_STATUS cCmdWrapMessageRead(UBYTE * ArgV[]) { NXT_STATUS Status = NO_ERR; NXT_STATUS AllocStatus = NO_ERR; UBYTE QueueID = *(UBYTE *)(ArgV[1]); DV_INDEX DestDVIndex = *(DV_INDEX *)(ArgV[3]); UWORD MessageSize; UBYTE i; NXT_ASSERT(IS_DV_INDEX_SANE(DestDVIndex)); //Check Next Message's size Status = cCmdMessageGetSize(QueueID, &MessageSize); //If there is a valid message in local mailbox, read it if (!IS_ERR(Status) && MessageSize > 0 ) { //!!! Also check for EMPTY_MAILBOX status? //Size destination string AllocStatus = cCmdDVArrayAlloc(DestDVIndex, MessageSize); if (IS_ERR(AllocStatus)) return AllocStatus; //Get Message //!!! Should more aggressively enforce null termination before blindly copying to dataspace Status = cCmdMessageRead(QueueID, cCmdDVPtr(DestDVIndex), MessageSize, *(ArgV[2])); } else { //Clear destination string AllocStatus = cCmdDVArrayAlloc(DestDVIndex, 1); if (IS_ERR(AllocStatus)) return AllocStatus; //Successful allocation, make sure first byte is null terminator *(UBYTE*)(cCmdDVPtr(DestDVIndex)) = '\0'; } //If there were no local messages, see if there are any waiting in our slaves' outboxes if (Status == STAT_MSG_EMPTY_MAILBOX && QueueID < INCOMING_QUEUE_COUNT) { //If there's an old error code hanging around, clear it before proceeding. //!!! Clearing error here means bytecode status checking loops could get false SUCCESS results? if (VarsCmd.CommStat < 0) VarsCmd.CommStat = SUCCESS; //Search through possible slaves, looking for valid connection for (i = 0; i < SIZE_OF_BT_CONNECT_TABLE - 1; i++) { //Advance CommCurrConnection and limit to 1, 2, or 3 (only slave connection slots are checked) VarsCmd.CommCurrConnection++; if (VarsCmd.CommCurrConnection == SIZE_OF_BT_CONNECT_TABLE) VarsCmd.CommCurrConnection = 1; if (cCmdBTCheckStatus(VarsCmd.CommCurrConnection) == NO_ERR) break; } //If there is at least one configured slave connection, make a remote read request if (i < SIZE_OF_BT_CONNECT_TABLE - 1) { //Outgoing QueueID on slave device is the local QueueID + INCOMING_QUEUE_COUNT RemoteMsgReadPacket[2] = QueueID + INCOMING_QUEUE_COUNT; RemoteMsgReadPacket[3] = QueueID; //Request comm module to send assembled packet and not go idle until response comes back (or error) pMapComm->pFunc(SENDDATA, sizeof(RemoteMsgReadPacket), VarsCmd.CommCurrConnection, TRUE, RemoteMsgReadPacket, (UWORD*)&(VarsCmd.CommStat)); //Read status back after SENDDATA call so bytecode gets STAT_COMM_PENDING or error Status = cCmdBTCheckStatus(VarsCmd.CommCurrConnection); //If our request was accepted, set the DirtyComm flag so stream will get cleaned up later if (Status == STAT_COMM_PENDING) VarsCmd.DirtyComm = TRUE; } } *(SBYTE *)(ArgV[0]) = Status; if (IS_FATAL(Status)) return Status; else return (NO_ERR); } // //cCmdWrapCommBTCheckStatus //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: Connection index, 0-3 // NXT_STATUS cCmdWrapCommBTCheckStatus(UBYTE * ArgV[]) { *((SBYTE*)(ArgV[0])) = cCmdBTCheckStatus(*(ArgV[1])); return (NO_ERR); } // //cCmdWrapCommBTWrite //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: Connection index, 0-3 //ArgV[2]: Buffer // NXT_STATUS cCmdWrapCommBTWrite(UBYTE * ArgV[]) { SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UBYTE Connection = *(ArgV[1]); UBYTE * pBuf; UWORD BufLength; DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[2]); pBuf = cCmdDVPtr(DVIndex); BufLength = DV_ARRAY[DVIndex].Count; //If there's an old error code hanging around, clear it before proceeding. if (VarsCmd.CommStat < 0) VarsCmd.CommStat = SUCCESS; //!!! Only first 256 bytes could possibly make it through! Should return error on longer input? //!!! Not requesting a wait-for-response because only known use doesn't read responses. pMapComm->pFunc(SENDDATA, (UBYTE)BufLength, Connection, FALSE, pBuf, (UWORD*)&(VarsCmd.CommStat)); //!!! Reasonable to wrap below code in cCmdCommBTCheckStatus? //INPROGRESS means our request was accepted by His Funkiness of pFunc if (VarsCmd.CommStat == (SWORD)INPROGRESS) { *pReturnVal = STAT_COMM_PENDING; //Set DirtyComm flag so stream is reset after program ends VarsCmd.DirtyComm = TRUE; } //Translate BTBUSY to ERR_COMM_CHAN_NOT_READY else if (VarsCmd.CommStat == (SWORD)BTBUSY) { *pReturnVal = ERR_COMM_CHAN_NOT_READY; } else { *pReturnVal = UNPACK_STATUS(VarsCmd.CommStat); } return (NO_ERR); } // //cCmdWrapCommBTRead //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: Count to read //ArgV[2]: Buffer // NXT_STATUS cCmdWrapCommBTRead(UBYTE * ArgV[]) { //SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); //UBYTE * pBuf = (ArgV[2]); //!!! should provide length and/or connection to read? //!!! This syscall is not implemented; return fatal error. return (ERR_INSTR); } // //cCmdWrapKeepAlive //ArgV[0]: (return) Current timer limit in ms, ULONG // NXT_STATUS cCmdWrapKeepAlive(UBYTE * ArgV[]) { pMapUi->Flags |= UI_RESET_SLEEP_TIMER; //Convert UI's minute-based timeout value to millisecs //Milliseconds are the "natural" time unit in user-land. *(ULONG*)(ArgV[0]) = (pMapUi->SleepTimeout * 60 * 1000); return (NO_ERR); } #define MAX_IOM_BUFFER_SIZE 64 // //cCmdWrapIOMapRead //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: Module name, CStr //ArgV[2]: Offset, UWORD //ArgV[3]: Count, UWORD //ArgV[4]: Buffer, UBYTE array // NXT_STATUS cCmdWrapIOMapRead(UBYTE * ArgV[]) { UWORD LStatus; NXT_STATUS Status; SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UWORD Offset = *(UWORD*)(ArgV[2]); //Our copy of 'Count' must be a ULONG to match the loader interface ULONG Count = *(UWORD*)(ArgV[3]); DV_INDEX DVIndex; //Buffer for return of FINDFIRSTMODULE call, structure defined in protocol doc //We need it to transfer the ModuleID to the IOMAPREAD call UBYTE FindBuffer[FILENAME_LENGTH + 10]; //Buffer to store data and offset in for IOMAPREAD call //!!! Constant size means only limited reads and writes UBYTE DataBuffer[MAX_IOM_BUFFER_SIZE + 2]; if (Count > MAX_IOM_BUFFER_SIZE) { //Request to read too much data at once; clear buffer, return error. DVIndex = *(DV_INDEX *)(ArgV[4]); *pReturnVal = cCmdDVArrayAlloc(DVIndex, 0); if (IS_ERR(*pReturnVal)) return (*pReturnVal); *pReturnVal = ERR_INVALID_SIZE; return (NO_ERR); } //Resolve module name DVIndex = *(DV_INDEX *)(ArgV[1]); ArgV[1] = cCmdDVPtr(DVIndex); //Find module by name. Note that wildcards are accepted, but only first match matters. LStatus = pMapLoader->pFunc(FINDFIRSTMODULE, ArgV[1], FindBuffer, NULL); if (LOADER_ERR(LStatus) == SUCCESS) { //Module was found, transfer Offset into first two bytes of DataBuffer and attempt to read *(UWORD*)(DataBuffer) = Offset; LStatus = pMapLoader->pFunc(IOMAPREAD, &(FindBuffer[FILENAME_LENGTH + 1]), DataBuffer, &Count); if (LOADER_ERR(LStatus) == SUCCESS) { //No error from IOMAPREAD, so copy the data into VM's dataspace //Size destination array DVIndex = *(DV_INDEX *)(ArgV[4]); Status = cCmdDVArrayAlloc(DVIndex, (UWORD)Count); if (IS_ERR(Status)) { //Alloc failed, so close handle and return pMapLoader->pFunc(CLOSEMODHANDLE, NULL, NULL, NULL); return (Status); } //Alloc succeeded, so resolve and copy away ArgV[4] = cCmdDVPtr(DVIndex); memcpy(ArgV[4], &(DataBuffer[2]), Count); } } *pReturnVal = LOADER_ERR_BYTE(LStatus); pMapLoader->pFunc(CLOSEMODHANDLE, NULL, NULL, NULL); return (NO_ERR); } // //cCmdWrapIOMapWrite //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: Module name, CStr //ArgV[2]: Offset, UWORD //ArgV[3]: Buffer, UBYTE array // NXT_STATUS cCmdWrapIOMapWrite(UBYTE * ArgV[]) { UWORD LStatus; SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UWORD Offset = *(UWORD*)(ArgV[2]); //Our copy of 'Count' must be a ULONG to match the loader interface ULONG Count; DV_INDEX DVIndex; //Buffer for return of FINDFIRSTMODULE call, structure defined in protocol doc //We need it to transfer the ModuleID to the IOMAPREAD call UBYTE FindBuffer[FILENAME_LENGTH + 10]; //Buffer to store data and offset in for IOMAPREAD call //!!! Constant size means only limited reads and writes UBYTE DataBuffer[MAX_IOM_BUFFER_SIZE + 2]; //Resolve module name and buffer DVIndex = *(DV_INDEX *)(ArgV[1]); ArgV[1] = cCmdDVPtr(DVIndex); DVIndex = *(DV_INDEX *)(ArgV[3]); ArgV[3] = cCmdDVPtr(DVIndex); Count = DV_ARRAY[DVIndex].Count; if (Count > MAX_IOM_BUFFER_SIZE) { //Request to read too much data at once; return error and give up *pReturnVal = ERR_INVALID_SIZE; return (NO_ERR); } LStatus = pMapLoader->pFunc(FINDFIRSTMODULE, ArgV[1], FindBuffer, NULL); if (LOADER_ERR(LStatus) == SUCCESS) { //Module was found, transfer Offset into first two bytes of DataBuffer, copy data into rest of buffer, then write *(UWORD*)(DataBuffer) = Offset; memcpy(&(DataBuffer[2]), ArgV[3], Count); LStatus = pMapLoader->pFunc(IOMAPWRITE, &(FindBuffer[FILENAME_LENGTH + 1]), DataBuffer, &Count); } *pReturnVal = LOADER_ERR_BYTE(LStatus); pMapLoader->pFunc(CLOSEMODHANDLE, NULL, NULL, NULL); return (NO_ERR); } #if VM_BENCHMARK void cCmdWriteBenchmarkFile() { LOADER_STATUS LStatus; UBYTE Handle; ULONG BenchFileSize; ULONG i, Length; UBYTE Buffer[256]; //Remove old benchmark file, create a new one strcpy((char *)Buffer, "benchmark.txt"); pMapLoader->pFunc(DELETE, Buffer, NULL, NULL); BenchFileSize = 2048; LStatus = pMapLoader->pFunc(OPENWRITEDATA, Buffer, NULL, &BenchFileSize); if (!LOADER_ERR(LStatus)) { //Write Benchmark file Handle = LOADER_HANDLE(LStatus); //Header sprintf((char *)Buffer, "Program Name: %s\r\n", VarsCmd.ActiveProgName); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "InstrCount: %d\r\n", VarsCmd.InstrCount); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "Time: %d\r\n", IOMapCmd.Tick - VarsCmd.StartTick); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "Instr/Tick: %d\r\n", VarsCmd.Average); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "CmdCtrl Calls: %d\r\n", VarsCmd.CmdCtrlCount); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "OverTime Rounds: %d\r\n", VarsCmd.OverTimeCount); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "Max OverTime Length: %d\r\n", VarsCmd.MaxOverTimeLength); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "CompactionCount: %d\r\n", VarsCmd.CompactionCount); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "LastCompactionTick: %d\r\n", VarsCmd.LastCompactionTick); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); sprintf((char *)Buffer, "MaxCompactionTime: %d\r\n", VarsCmd.MaxCompactionTime); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); //opcode benchmarks sprintf((char *)Buffer, "Op\tCnt\tOver\tMax\r\n"); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); for (i = 0; i < OPCODE_COUNT; i++) { sprintf((char *)Buffer, "%x\t%d\t%d\t%d\t%d\r\n", i, VarsCmd.OpcodeBenchmarks[i][0], VarsCmd.OpcodeBenchmarks[i][1], VarsCmd.OpcodeBenchmarks[i][2], VarsCmd.OpcodeBenchmarks[i][3]); Length = strlen((char *)Buffer); LStatus = pMapLoader->pFunc(WRITE, &Handle, Buffer, &Length); } //close file LStatus = pMapLoader->pFunc(CLOSE, &Handle, NULL, NULL); } } #endif ///////////////////////////////////////////////////////////// // Dymanic syscall implementations //////////////////////////////////////////////////////////// // //cCmdWrapDatalogWrite //ArgV[0]: (return) Error Code, SBYTE (NXT_STATUS) //ArgV[1]: Message, CStr // NXT_STATUS cCmdWrapDatalogWrite(UBYTE * ArgV[]) { NXT_STATUS Status = NO_ERR; DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[1]); ArgV[1] = cCmdDVPtr(DVIndex); Status = cCmdDatalogWrite(ArgV[1], DV_ARRAY[DVIndex].Count); *(SBYTE *)(ArgV[0]) = Status; if (IS_FATAL(Status)) return Status; else return (NO_ERR); } // //cCmdWrapDatalogGetTimes //ArgV[0]: SyncTime, U32 //ArgV[1]: SyncTick, U32 // NXT_STATUS cCmdWrapDatalogGetTimes(UBYTE * ArgV[]) { *((ULONG *)ArgV[1]) = IOMapCmd.SyncTime; *((ULONG *)ArgV[2]) = IOMapCmd.SyncTick; return (NO_ERR); } // //cCmdWrapSetSleepTimeout //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: desired timer limit in ms, ULONG // NXT_STATUS cCmdWrapSetSleepTimeout(UBYTE * ArgV[]) { ULONG value = *(ULONG*)(ArgV[1]); if(value==0) { pMapUi->SleepTimeout=0; } else if(value < 60000) { pMapUi->SleepTimeout=1; //integer math would've made this zero } else { pMapUi->SleepTimeout= value / 60000; } return (NO_ERR); } // currently copied from LS, not finished. // //cCmdWrapCommHSWrite //ArgV[0]: (return) Status code, SBYTE //ArgV[1]: Port specifier, UBYTE //ArgV[2]: Buffer to send, UBYTE array, only SIZE_OF_LSBUF bytes will be used //ArgV[3]: ResponseLength, UBYTE, specifies expected bytes back from slave device // NXT_STATUS cCmdWrapCommHSWrite(UBYTE * ArgV[]) { SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UBYTE Port = *(ArgV[1]); UBYTE * pBuf; UWORD BufLength; UBYTE ResponseLength = *(ArgV[3]); DV_INDEX DVIndex; //Resolve array arguments DVIndex = *(DV_INDEX *)(ArgV[2]); pBuf = cCmdDVPtr(DVIndex); BufLength = DV_ARRAY[DVIndex].Count; *pReturnVal = cCmdLSWrite(Port, (UBYTE)BufLength, pBuf, ResponseLength); return (NO_ERR); } // //cCmdWrapCommHSCheckStatus //ArgV[0]: (return) Status code, SBYTE //ArgV[1]: Port specifier, UBYTE //ArgV[2]: BytesReady, UBYTE // NXT_STATUS cCmdWrapCommHSCheckStatus(UBYTE * ArgV[]) { UBYTE Port = *(ArgV[1]); *((SBYTE*)(ArgV[0])) = cCmdLSCheckStatus(Port); *((UBYTE*)(ArgV[2])) = cCmdLSCalcBytesReady(Port); return (NO_ERR); } // //cCmdWrapCommHSRead //ArgV[0]: (return) Status code, SBYTE //ArgV[1]: Port specifier, UBYTE //ArgV[2]: Buffer for data, UBYTE array, max SIZE_OF_LSBUF bytes will be written //ArgV[3]: BufferLength, UBYTE, specifies size of buffer requested // NXT_STATUS cCmdWrapCommHSRead(UBYTE * ArgV[]) { SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UBYTE Port = *(ArgV[1]); UBYTE * pBuf; UBYTE BufLength = *(ArgV[3]); UBYTE BytesToRead; DV_INDEX DVIndex = *(DV_INDEX *)(ArgV[2]); NXT_STATUS AllocStatus; *pReturnVal = cCmdLSCheckStatus(Port); BytesToRead = cCmdLSCalcBytesReady(Port); //If channel is OK and has data ready for us, put the data into outgoing buffer if (!IS_ERR(*pReturnVal) && BytesToRead > 0) { //Limit buffer to available data if (BufLength > BytesToRead) BufLength = BytesToRead; AllocStatus = cCmdDVArrayAlloc(DVIndex, BufLength); if (IS_ERR(AllocStatus)) return (AllocStatus); pBuf = cCmdDVPtr(DVIndex); *pReturnVal = cCmdLSRead(Port, BufLength, pBuf); } //Else, the channel has an error and/or there's no data to read; clear the output array else { AllocStatus = cCmdDVArrayAlloc(DVIndex, 0); if (IS_ERR(AllocStatus)) return (AllocStatus); } return (NO_ERR); } // //cCmdWrapCommBTOnOff //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: Power State, 0-1 // NXT_STATUS cCmdWrapCommBTOnOff(UBYTE * ArgV[]) { UWORD retVal; NXT_STATUS status; SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UBYTE powerState = *(ArgV[1]); if(powerState) status= pMapComm->pFunc(BTON, 0, 0, 0, NULL, &retVal); else status= pMapComm->pFunc(BTOFF, 0, 0, 0, NULL, &retVal); *pReturnVal= (status == SUCCESS) ? retVal : status; return (NO_ERR); } // //cCmdWrapCommBTConnection //ArgV[0]: (return) Status byte, SBYTE //ArgV[1]: Action, UBYTE //ArgV[2]: name, UBYTE array CStr //ArgV[3]: connection slot, UBYTE // NXT_STATUS cCmdWrapCommBTConnection(UBYTE * ArgV[]) { UWORD retVal; NXT_STATUS status; SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); UBYTE *nmPtr; UBYTE action = *(ArgV[1]); UBYTE connection = *(ArgV[3]); nmPtr = cCmdDVPtr(*(DV_INDEX *)(ArgV[2])); if(action) // Init status= pMapComm->pFunc(CONNECTBYNAME, 0, connection, 0, nmPtr, &retVal); else // Close status= pMapComm->pFunc(DISCONNECT, connection, 0, 0, NULL, &retVal); *pReturnVal= (status == SUCCESS) ? retVal : status; return (NO_ERR); } // //cCmdWrapReadSemData //ArgV[0]: return data, U8 //ArgV[1]: which (0=used, 1=request), U8 // NXT_STATUS cCmdWrapReadSemData(UBYTE * ArgV[]) { if(!(*((UBYTE *)ArgV[1]))) *((UBYTE *)ArgV[0])= gUsageSemData; else *((UBYTE *)ArgV[0])= gRequestSemData; return (NO_ERR); } // //cCmdWrapWriteSemData //ArgV[0]: return data, U8 //ArgV[1]: which (0=used, 1=request), U8 //ArgV[2]: newValue, U8 //ArgV[3]: action (0= OR, 1= AND), U8 // NXT_STATUS cCmdWrapWriteSemData(UBYTE * ArgV[]) { UBYTE curVal, newVal, which= (*((UBYTE *)ArgV[1])); if(!which) curVal= gUsageSemData; else curVal= gRequestSemData; newVal= *((UBYTE *)ArgV[2]); if(*((UBYTE *)ArgV[3])) curVal &= ~newVal; else curVal |= newVal; if(!which) gUsageSemData= curVal; else gRequestSemData= curVal; *((UBYTE *)ArgV[0])= curVal; return (NO_ERR); } // //cCmdWrapUpdateCalibCacheInfo //ArgV[0]: return data, U8 //ArgV[1]: nm, UBYTE array CStr //ArgV[2]: min, U16 //ArgV[3]: max , U16 // NXT_STATUS cCmdWrapUpdateCalibCacheInfo(UBYTE * ArgV[]) { UBYTE *nm= cCmdDVPtr(*(DV_INDEX *)(ArgV[1])); SWORD min= (*((SWORD *)ArgV[2])); SWORD max= (*((SWORD *)ArgV[3])); cCmdUpdateCalibrationCache(nm, min, max); *((UBYTE *)ArgV[0])= SUCCESS; return (NO_ERR); } // //cCmdWrapComputeCalibValue //ArgV[0]: return data, U8 //ArgV[1]: nm, UBYTE array CStr //ArgV[2]: raw, U16 ref in out NXT_STATUS cCmdWrapComputeCalibValue (UBYTE * ArgV[]) { UBYTE *nm= cCmdDVPtr(*(DV_INDEX *)(ArgV[1])); SWORD raw= (*((SWORD *)ArgV[2])); *((UBYTE *)ArgV[0])= cCmdComputeCalibratedValue(nm, &raw); (*((SWORD *)ArgV[2]))= raw; return (NO_ERR); } typedef struct { SWORD min, max; UBYTE nm[FILENAME_LENGTH + 1]; } CalibCacheType; SBYTE gCalibCacheCnt= 0; DV_INDEX gCalibCacheArrayDVIdx= NOT_A_DS_ID; CalibCacheType *gCalibCacheArray= NULL; SWORD cCmdGetCalibrationIndex(UBYTE *nm) { SBYTE i; for(i= 0; i < gCalibCacheCnt; i++) if(!strcmp((PSZ)nm, (PSZ)gCalibCacheArray[i].nm)) break; return i; } NXT_STATUS cCmdComputeCalibratedValue(UBYTE *nm, SWORD *pRaw) { SBYTE i= cCmdGetCalibrationIndex(nm); NXT_STATUS status= ERR_RC_ILLEGAL_VAL; SLONG raw= *pRaw, range; if(i < gCalibCacheCnt) { status= SUCCESS; raw -= gCalibCacheArray[i].min; range= (gCalibCacheArray[i].max - gCalibCacheArray[i].min); } else range= 1023; raw *= 100; raw /= range; if(raw < 0) raw= 0; else if(raw > 100) raw= 100; *pRaw= raw; return status; } NXT_STATUS ResizeCalibCache(ULONG elements) { // alloc dv if needed, grow if needed. dv never freed. on boot, set to NOT_A_DS_ID. use cnt for valid elements. NXT_STATUS Status = NO_ERR; if(gCalibCacheArrayDVIdx == NOT_A_DS_ID) Status = cCmdAllocDopeVector(&gCalibCacheArrayDVIdx, sizeof(CalibCacheType)); if(!IS_ERR(Status) && DV_ARRAY[gCalibCacheArrayDVIdx].Count < elements) //Allocate storage for cache element Status = cCmdDVArrayAlloc(gCalibCacheArrayDVIdx, elements); if(!IS_ERR(Status)) gCalibCacheArray= cCmdDVPtr(gCalibCacheArrayDVIdx); // on error, does old DVIdx still point to array, or should we null out array??? return Status; } // called to update min/max on existing cache element, and to add new named element void cCmdUpdateCalibrationCache(UBYTE *nm, SWORD min, SWORD max) { SWORD i= cCmdGetCalibrationIndex(nm); NXT_STATUS Status = NO_ERR; if(i == gCalibCacheCnt) { // sensor wasn't found, insert into cache Status= ResizeCalibCache(gCalibCacheCnt+1); if(!IS_ERR(Status)) { gCalibCacheCnt++; strcpy((PSZ)gCalibCacheArray[i].nm, (PSZ)nm); } } if(!IS_ERR(Status)) { gCalibCacheArray[i].min= min; gCalibCacheArray[i].max= max; } } void cCmdLoadCalibrationFiles(void) { ULONG cnt, DataSize; UBYTE nm[FILENAME_LENGTH + 1], nmLen; SWORD Handle, HandleSearch; gCalibCacheCnt= 0; gCalibCacheArrayDVIdx= NOT_A_DS_ID; // file I/O to load all .cal files into cached globals used by scaling syscall HandleSearch = pMapLoader->pFunc(FINDFIRST, (UBYTE *)"*.cal", nm, &cnt); // returns total files and nm of first one while (LOADER_ERR(HandleSearch) == SUCCESS) { // if we have a file, process it by closing and opening SWORD min= 0, max= 0, tmp; ULONG length; pMapLoader->pFunc(CLOSE, LOADER_HANDLE_P(HandleSearch), NULL, NULL); Handle = pMapLoader->pFunc(OPENREAD, nm, NULL, &DataSize); if (LOADER_ERR(Handle) == SUCCESS && DataSize == 4) { // access data, two bytes for min and two for max length= 2; pMapLoader->pFunc(READ,LOADER_HANDLE_P(Handle),(UBYTE*)&tmp,&length); if (length == 2) min= tmp; length= 2; pMapLoader->pFunc(READ,LOADER_HANDLE_P(Handle),(UBYTE*)&tmp,&length); if (length == 2) max= tmp; } pMapLoader->pFunc(CLOSE, LOADER_HANDLE_P(Handle), NULL, NULL); // update calibration cache with nm, min, and max nmLen= strlen((PSZ)nm) - 4; // chop off .cal extension nm[nmLen]= 0; cCmdUpdateCalibrationCache(nm, min, max); HandleSearch = pMapLoader->pFunc(FINDNEXT, LOADER_HANDLE_P(HandleSearch), nm, &cnt); } pMapLoader->pFunc(CLOSE, LOADER_HANDLE_P(HandleSearch), NULL, NULL); } // //cCmdWrapListFiles //ArgV[0]: return data, SBYTE //ArgV[1]: pattern, UBYTE array CStr //ArgV[2]: list, UBYTE array CStr array ref in out NXT_STATUS cCmdWrapListFiles (UBYTE * ArgV[]) { ULONG fileSize, matchCount=0, i=0, oldCount; SWORD HandleSearch; NXT_STATUS Status = NO_ERR; DV_INDEX listIdx, *list; UBYTE *strTemp, *pattern; UBYTE name[FILENAME_LENGTH + 1]; //Resolve array arguments pattern = cCmdDVPtr(*(DV_INDEX *)(ArgV[1])); listIdx = *(DV_INDEX *)(ArgV[2]); HandleSearch = pMapLoader->pFunc(FINDFIRST, pattern, name, &fileSize); // returns first file matching pattern //Count how many files we're going to have while (LOADER_ERR(HandleSearch) == SUCCESS) { matchCount++; pMapLoader->pFunc(CLOSE, LOADER_HANDLE_P(HandleSearch), NULL, NULL); HandleSearch = pMapLoader->pFunc(FINDNEXT, LOADER_HANDLE_P(HandleSearch), name, &fileSize); } HandleSearch = pMapLoader->pFunc(FINDFIRST, pattern, name, &fileSize); // returns first file matching pattern oldCount = DV_ARRAY[listIdx].Count; // Check to see how many dope vectors are already in the array (if they passed us a non-blank array of strings) Status = cCmdDVArrayAlloc(listIdx, matchCount); // Size the top-level array if(IS_ERR(Status)) return Status; list = (DV_INDEX*)(VarsCmd.pDataspace + DV_ARRAY[listIdx].Offset); // Get a pointer into the dataspace for the array of DV_INDEXes while (LOADER_ERR(HandleSearch) == SUCCESS && !IS_ERR(Status)) { pMapLoader->pFunc(CLOSE, LOADER_HANDLE_P(HandleSearch), NULL, NULL); // Close the handle that we automatically opened above // Allocate a new dope vector if one doesn't already exist if(i >= oldCount) Status = cCmdAllocDopeVector(&(list[i]), sizeof(char)); // Allocate the string buffer for output array[i] if(!IS_ERR(Status)) Status = cCmdDVArrayAlloc(list[i], strlen((PSZ)name) + 1); if(!IS_ERR(Status)) { strTemp = VarsCmd.pDataspace + DV_ARRAY[list[i]].Offset; // Get a pointer into the dataspace for this string strcpy((PSZ)strTemp, (PSZ)name); } i++; HandleSearch = pMapLoader->pFunc(FINDNEXT, LOADER_HANDLE_P(HandleSearch), name, &fileSize); } *(SBYTE *)(ArgV[0]) = Status; return Status; } #ifdef SIM_NXT // Accessors for simulator library code SWORD cCmdGetCodeWord(CLUMP_ID Clump, CODE_INDEX Index) { if (Clump == NOT_A_CLUMP) { NXT_ASSERT(Index < VarsCmd.CodespaceCount); return (VarsCmd.pCodespace[Index]); } else { NXT_ASSERT(cCmdIsClumpIDSane(Clump)); #error // CodeStart is now absolute, but not sure how to fix return (((SWORD)VarsCmd.pCodespace[VarsCmd.pAllClumps[Clump].CodeStart + Index])); } } UBYTE * cCmdGetDataspace(UWORD *DataspaceSize) { if (DataspaceSize) *DataspaceSize = VarsCmd.DataspaceSize; return (VarsCmd.pDataspace); } DOPE_VECTOR * cCmdGetDopeVectorPtr() { return VarsCmd.MemMgr.pDopeVectorArray; } MEM_MGR cCmdGetMemMgr(void) { return VarsCmd.MemMgr; } ULONG cCmdGetPoolSize() { return VarsCmd.PoolSize; } #endif #else //!ENABLE_VM // //Implementations of standard interface if VM is disabled. //Place low-level test code here if VM is causing issues. //Test code must implement cCmdInit(), cCmdCtrl(), and cCmdExit() at a minimum. //Recommend using a pattern like #include "c_cmd_alternate.c" // //!!! !ENABLE_VM implementations really should provide a placeholder function for this pointer //IOMapCmd.pRCHandler = &cCmdHandleRemoteCommands; #include "c_cmd_alternate.c" #endif //ENABLE_VM nxt-firmware-1.29.7/src/c_cmd.h000066400000000000000000000720551466344546000162670ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date: 10-07-08 13:22 $ // // Filename $Workfile:: c_cmd.h $ // // Version $Revision: 8 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_cmd. $ // // Platform C // // // File Description: // This file contains definitions and prototypes for the VM which runs bytecode // programs compatible with LEGO MINDSTORMS NXT Software 1.0. // #ifndef C_CMD #define C_CMD //!!! MAX_HANDLES also defined in m_sched.h #ifndef MAX_HANDLES #define MAX_HANDLES 16 #endif #include "c_cmd_bytecodes.h" #define SYSCALL_COUNT 48 extern const HEADER cCmd; // //Standard interface to other modules // void cCmdInit(void* pHeader); void cCmdCtrl(void); void cCmdExit(void); // //ARM_NXT vs SIM_NXT //These definitions are set up to allow compiling this code for use in a simulated (non-ARM7) environment. //If your toolchain doesn't automatically use the __ICCARM__ or __arm__ token, define it to ensure normal compilation. // #if defined (__ICCARM__) || (defined (__GNUC__) && defined (__arm__)) #define ARM_NXT #else #define SIM_NXT #endif // //ENABLE_VM toggles compilation the main body of VM code. //Define it as 0 to compile alternate implementation for testing (see bottom of c_cmd.c) // #define ENABLE_VM 1 #undef ARM_DEBUG // //VM_BENCHMARK enables extra instrumentation code to measure VM performance. //When enabled, a file named "benchmark.txt" is produced every time a program completes. // #define VM_BENCHMARK (ENABLE_VM && 0) //<-- Toggle to turn on benchmark calculations #if VM_BENCHMARK //Prototype for benchmark recording function void cCmdWriteBenchmarkFile(); #endif // //Run-time assert macros //Use these to test for unexpected conditions //If expr evaluates as false while running under a debugger, // a software breakpoint exception is thrown. //NXT_BREAK is just a shortcut for unconditional break. // //Assert definitions behind WIN_DEBUG only make sense when compiling SIM_NXT // under an x86 Windows debugger. // #if defined WIN_DEBUG //"int 3" is a break exception on x86 #define NXT_ASSERT(expr) if (expr) {} else { __asm {int 3} } #define NXT_BREAK NXT_ASSERT(0) // //Assert definitions behind ARM_DEBUG aren't quite as handy as WIN_DEBUG, // but they do record the code line causing the last assert failure. // #elif defined(ARM_DEBUG) #define NXT_ASSERT(expr) if (expr) {}\ else\ {\ VarsCmd.AssertFlag = TRUE;\ VarsCmd.AssertLine = __LINE__;\ } #define NXT_BREAK NXT_ASSERT(0); #else //Not debugging, so #defined as nothing //!!! Note that these definitions means all usages of NXT_ASSERT and NXT_BREAK // get stripped out of an unmodified ARM7 build. //Unless ARM_DEBUG is enabled, treat them as documentation of expected values. #define NXT_ASSERT(expr) #define NXT_BREAK #endif // //Status byte used to return requests for further action or errors //Valid codes #defined in c_cmd.iom //!!!JLOFTUS Replace with NXT_STATUS? Same for ASSERTS? Others? Risk factors? // typedef SBYTE NXT_STATUS; #if ENABLE_VM //Intial values for clump records are packed into 4 bytes in the file format. #define VM_FILE_CLUMP_REC_SIZE 4 // // Definitions for dataspace management, IO Map (IOM) access, and bytecode instruction structure // //Type codes for use in the dataspace table-of-contents (DSTOC) typedef UBYTE TYPE_CODE; enum { //VOID type for unused DS elements; never valid to address them from bytecode TC_VOID, //Simple scalar integers, equivalent to matching basic types from stdconst.h TC_UBYTE, TC_SBYTE, TC_UWORD, TC_SWORD, TC_ULONG, TC_SLONG, TC_LAST_INT_SCALAR= TC_SLONG, //Aggregate types containing one or more scalar TC_ARRAY, TC_CLUSTER, //Mutex tracks current holder and any waiting clumps TC_MUTEX, TC_FLOAT, TC_LAST_VALID= TC_FLOAT }; //Sizes (in bytes) of each scalar type #define SIZE_UBYTE 1 #define SIZE_SBYTE 1 #define SIZE_UWORD 2 #define SIZE_SWORD 2 #define SIZE_ULONG 4 #define SIZE_SLONG 4 #define SIZE_FLOAT 4 //MUTEX record is a struct containing 3 8-bit CLUMP_IDs, packed into 32-bit word //See MUTEX_Q typedef #define SIZE_MUTEX 4 //Module IDs for IO map addressing enum { MOD_INPUT, MOD_OUTPUT }; //Field IDs for input IOM enum { IO_IN_TYPE, IO_IN_MODE, IO_IN_ADRAW, IO_IN_NORMRAW, IO_IN_SCALEDVAL, IO_IN_INVALID_DATA }; //FPP = Fields Per Port #define IO_IN_FPP 6 #define IO_IN_FIELD_COUNT (IO_IN_FPP * NO_OF_INPUTS) //Field IDs for input IOM enum { IO_OUT_FLAGS, IO_OUT_MODE, IO_OUT_SPEED, //AKA "Power" IO_OUT_ACTUAL_SPEED, IO_OUT_TACH_COUNT, IO_OUT_TACH_LIMIT, IO_OUT_RUN_STATE, IO_OUT_TURN_RATIO, IO_OUT_REG_MODE, IO_OUT_OVERLOAD, IO_OUT_REG_P_VAL, IO_OUT_REG_I_VAL, IO_OUT_REG_D_VAL, IO_OUT_BLOCK_TACH_COUNT, IO_OUT_ROTATION_COUNT, IO_OUT_OPTIONS, IO_OUT_MAX_SPEED, IO_OUT_MAX_ACCELERATION, }; #define IO_OUT_FPP 18 #define IO_OUT_FIELD_COUNT (IO_OUT_FPP * NO_OF_OUTPUTS) // //DS_TOC_ENTRY is a record in the dataspace table of contents //The TypeCode describes the data which is stored at Dataspace[DSOffset] // typedef struct { TYPE_CODE TypeCode; UBYTE Flags; SWORD DSOffset; } DS_TOC_ENTRY; //DS_TOC_ENTRY Flags //!!! Yes, there's only one flag defined for an 8-bit field. //ARM7 alignment rules means those bits would otherwise just be padding, anyway. #define DS_DEFAULT_DEFAULT 1 //This entry has no default value in file; fill with zero at activation time //DS_ELEMENT_ID (AKA "DS item ID") indexes DataspaceTOC typedef UWORD DS_ELEMENT_ID; //Special flag value used for opcode-specific default behavior when real dataspace argument is not provided #define NOT_A_DS_ID 0xFFFF //Macro to bump DS_ELEMENT_IDs +1 with a cast (mostly to quash annoying warnings) #define INC_ID(X) ((DS_ELEMENT_ID)(X + 1)) //DATA_ARG may contain a DS_ELEMENT_ID or encoded IO map address typedef UWORD DATA_ARG; //CODE_WORD is a single indexable element of the codespace typedef SWORD CODE_WORD; //CODE_INDEX indexes codespaces for opcodes and args //!!! UWORD CODE_INDEX currently limits programs to 128KB code // Yes, this is "plenty", but noted here to make sure we think about it // when considering code size changes typedef UWORD CODE_INDEX; //Typedef and define to hold and check for valid file handles typedef UBYTE FILE_HANDLE; #define NOT_A_HANDLE 0xFF // // Dynamic Memory Manager // typedef UWORD DV_INDEX; //Dope Vector Index: Index into the DopeVectorArray //DOPE_VECTOR struct: One instance exists in the DopeVectorArray for every array in the dataspace. typedef struct { UWORD Offset; UWORD ElemSize; UWORD Count; DV_INDEX BackLink; // points to previous DV DV_INDEX Link; // points to next DV } DOPE_VECTOR; // //MEM_MGR struct //Head and Tail keep track of the main linked-list of dope vectors, // which must be maintained in ascending order according to Offset //FreeHead is the head DV of the list of allocated but unused DVs //pDopeVectorArray is initialized at activation-time to point to the master DVA // typedef struct { DV_INDEX Head; DV_INDEX Tail; DV_INDEX FreeHead; DOPE_VECTOR * pDopeVectorArray; } MEM_MGR; //Macro to shorten common DVA access code #define DV_ARRAY VarsCmd.MemMgr.pDopeVectorArray //# of nodes to alloc when the Dope Vector Array is full #define DV_ARRAY_GROWTH_COUNT 25 //Flag value for invalid Offset fields in DVs #define NOT_AN_OFFSET 0xFFFF //Check for legal index into DVA #define IS_DV_INDEX_SANE(X) (((X) > 0) && ((X) < DV_ARRAY[0].Count)) // // Message Queuing // // //There are 10 incoming and 10 outgoing message queues, each 5 messages deep //A "message" is defined as a null-terminated string under MAX_MESSAGE_SIZE // #define MESSAGES_PER_QUEUE 5 #define MESSAGE_QUEUE_COUNT 20 #define INCOMING_QUEUE_COUNT ((MESSAGE_QUEUE_COUNT)/2) #define NOT_A_QUEUE 0xFF // //MAX_MESSAGE_SIZE including null-terminator //!!! Capped at 59 unless USB protocol assumptions are changed! // #define MAX_MESSAGE_SIZE 59 //A MESSAGE is a dynamically sized string, so we use a DV_INDEX to get to its information typedef DV_INDEX MESSAGE; // //MESSAGE_QUEUE keeps track of last messages read and written (acts as a circular buffer) // typedef struct { UWORD ReadIndex; UWORD WriteIndex; MESSAGE Messages[MESSAGES_PER_QUEUE]; } MESSAGE_QUEUE; //Handy macros for accessing MESSAGE_QUEUEs #define GET_WRITE_MSG(QueueID) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].WriteIndex]) #define GET_READ_MSG(QueueID) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].ReadIndex]) #define SET_WRITE_MSG(QueueID, DVIndex) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].WriteIndex] = (DVIndex)) #define SET_READ_MSG(QueueID, DVIndex) (VarsCmd.MessageQueues[(QueueID)].Messages[VarsCmd.MessageQueues[(QueueID)].ReadIndex] = (DVIndex)) // // Datalog Queuing // // The datalog queue is loosely modeled around the message queue except that there is only one queue, not an array of them. // // A datalog has one less byte of 'header' info so different max size #define MAX_DATALOG_SIZE 60 // The number of datalog messages to buffer #define DATALOG_QUEUE_DEPTH 30 // A DATALOG_MESSAGE is a dynamically sized string, so we use a DV_INDEX to get to its information typedef DV_INDEX DATALOG_MESSAGE; // // DATALOG_QUEUE keeps track of last messages read and written (acts as a circular buffer) typedef struct { UWORD ReadIndex; UWORD WriteIndex; DATALOG_MESSAGE Datalogs[DATALOG_QUEUE_DEPTH]; } DATALOG_QUEUE; //Handy macros for accessing the DATALOG_QUEUE #define GET_WRITE_DTLG() (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.WriteIndex]) #define GET_READ_DTLG() (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.ReadIndex]) #define SET_WRITE_DTLG(DVIndex) (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.WriteIndex] = (DVIndex)) #define SET_READ_DTLG(DVIndex) (VarsCmd.DatalogBuffer.Datalogs[VarsCmd.DatalogBuffer.ReadIndex] = (DVIndex)) // //Definitions related to dataflow scheduling // //CLUMP_IDs are used to index list at pAllClumps typedef UBYTE CLUMP_ID; // //The last value in CLUMP_ID's range is reserved as NOT_A_CLUMP //This is useful as a queue terminator and general placeholder // #define NOT_A_CLUMP 0xFF #define MAX_CLUMPS 255 #define INSTR_MAX_COUNT 20 //CLUMP_Q struct for tracking head and tail of a queue of clumps typedef struct { CLUMP_ID Head; CLUMP_ID Tail; } CLUMP_Q; // //MUTEX_Q is a struct to be stashed in the dataspace to track state of a mutex //If mutex is free, Owner field is NOT_A_CLUMP and WaitQ is empty. //The mutex is acquired by stashing a new owner's ID. //If others attempt to acquire, they will be put on the WaitQ // typedef struct { CLUMP_ID Owner; CLUMP_Q WaitQ; } MUTEX_Q; // // Clump Record, run-time book-keeping for each clump // // CodeStart: Start of this clump's bytecodes, absolute address // CodeEnd: End of this clump's bytecodes, absolute address // PC: "program counter" -- current offset into codespace relative to CodeStart // InitFireCount: Initial count of upstream dependencies // CurrFireCount: Run-time count of unsatisfied dependencies // Link: ID of next clump in the queue. NOT_A_CLUMP denotes end or bad link. // // clumpScalarDispatchHints: this clump only uses scalar data args, can be interpretted with faster dispatch tables // // pDependents: pointer to list of downstream dependents' ClumpIDs // awakenTime: If a clump is on rest queue for sleep, this is the time at which it will return to runQueue // DependentCount: Count of downstream dependents // typedef struct { CODE_WORD* CodeStart; CODE_WORD* CodeEnd; CODE_WORD* PC; UBYTE InitFireCount; UBYTE CurrFireCount; //AKA ShortCount CLUMP_ID Link; UBYTE clumpScalarDispatchHints; CLUMP_ID* pDependents; ULONG awakenTime; UBYTE DependentCount; } CLUMP_REC; // //Definitions for memory pool management // //First valid pointer into the memory pool #define POOL_START ((UBYTE*)(VarsCmd.Pool)) //Sentinel points one byte *past* the pool -- i.e. first bad pool pointer #define POOL_SENTINEL ((UBYTE*)(VarsCmd.Pool + VarsCmd.PoolSize)) //Alignment mod for Pool and all sub-fields of the Pool #define POOL_ALIGN SIZE_SLONG #define ALIGN_TO_MOD(val,mod) if ((val) % (mod) != 0) { (val) += (mod) - ((val) % (mod)); } else {} // //Internal states of the VM //VM_IDLE: Just sitting around. Request to run program will lead to ONE of the VM_RUN* states. //VM_RUN_FREE: Attempt to run as many instructions as possible within our timeslice //VM_RUN_SINGLE: Run exactly one instruction per timeslice //VM_RUN_PAUSE: Program still "active", but someone has asked us to pause //VM_RESET2: Final clean up and return to IDLE //VM_RESET1: Initialize state variables and some I/O devices -- executed when programs end // typedef enum { VM_IDLE, VM_RUN_FREE, VM_RUN_SINGLE, VM_RUN_PAUSE, VM_RESET1, VM_RESET2, } VM_STATE; // // VARSCMD: Private state data for active program and VM system // //pCodespace: pointer for flat codespace (stored in flash, includes all clumps) //CodespaceCount: count of code words // //pAllClumps: Pointer to list of CLUMP_RECs //AllClumpsCount: Count of CLUMP_RECs in list // //RunQ: Head and tail of run queue (elements in-place in AllClumps list) // //pDataspaceTOC: Pointer to DSTOC entries (stored in flash) //DataspaceCount: Count of entries in DSTOC //pDataspace: Base pointer of actual dataspace //DataspaceSize: Size, in bytes, of dataspace //DSStaticSize: Size, in bytes, of static portion of the dataspace (used as an offset to the dynamic dataspace) // //VMState: Internal state of VM's loader/scheduler (cCmdCtrl()) // //MemMgr: Contains data to manage dynamic arrays // //PoolSize: Current size of main memory pool, in bytes. //Pool: Static pool of bytes for stashing all program run-time data // //ActiveProgHandle: Handle of the program that is currently running //ActiveProgName: Stashed name of currently running program, if any // //FileHandleTable: Table of file names opened by program while running. // First byte of each record is 'r' or 'w' (read or write). // //MessageQueues: Message buffer tracking data // //CommStat, CommStatReset, CommCurrConnection, DirtyComm: Helper data for interfacing to c_comm module // //DirtyDisplay: Boolean reminding us to re-initialize the display if program used it // //StartTick: MS tick stashed when program started. Used for relative time measurements. // //Further notes on the memory pool: // The main memory pool is used for all clump records, dataspace tracking data, // and the dataspace itself. In other words, pAllClumps and // pDataspace must all point to memory within the pool. Watch for NXT_ASSERTs // to enforce safe indexing into the pool. // typedef struct { CODE_WORD* pCodespace; CLUMP_REC* pAllClumps; DS_TOC_ENTRY* pDataspaceTOC; UBYTE* pDataspace; UBYTE* Pool; ULONG PoolSize; UWORD CodespaceCount; CLUMP_ID AllClumpsCount; UWORD DataspaceCount; UWORD DataspaceSize; UWORD DSStaticSize; VM_STATE VMState; MEM_MGR MemMgr; CLUMP_Q RunQ; CLUMP_Q RestQ; UBYTE ActiveProgHandle; UBYTE ActiveProgName[FILENAME_LENGTH + 1]; UBYTE FileHandleTable[MAX_HANDLES][FILENAME_LENGTH + 2]; MESSAGE_QUEUE MessageQueues[MESSAGE_QUEUE_COUNT]; SWORD CommStat; SWORD CommStatReset; UBYTE CommCurrConnection; UBYTE DirtyComm; UBYTE DirtyDisplay; ULONG StartTick; DATALOG_QUEUE DatalogBuffer; #if VM_BENCHMARK ULONG InstrCount; ULONG Average; ULONG OverTimeCount; ULONG MaxOverTimeLength; ULONG CmdCtrlCount; ULONG CompactionCount; ULONG LastCompactionTick; ULONG MaxCompactionTime; ULONG OpcodeBenchmarks[OPCODE_COUNT][4]; ULONG SyscallBenchmarks[SYSCALL_COUNT][4]; UBYTE Buffer[256]; #endif #if defined ARM_DEBUG UBYTE AssertFlag; ULONG AssertLine; #endif } VARSCMD; // //Activation // //Activate new program by filename (open file and inflate run-time data) NXT_STATUS cCmdActivateProgram(UBYTE * pFileName); //Deactivate currently active program (re-init run-time data and close file) void cCmdDeactivateProgram(); //Reset various device state variables void cCmdResetDevices(void); //Parse activation record file header information typedef struct { UWORD DSTOC; UWORD DSDefaults; UWORD DSDefaultsSize; UWORD DynamicDefaults; UWORD DynamicDefaultsSize; UWORD Clumps; UWORD Codespace; } PROG_FILE_OFFSETS; NXT_STATUS cCmdReadFileHeader(UBYTE* pData, ULONG DataSize, PROG_FILE_OFFSETS* pFileOffsets); NXT_STATUS cCmdInflateDSDefaults(UBYTE* pDSDefaults, UWORD *pDefaultsOffset, DS_ELEMENT_ID DSElementID); // //Clump management // //Clump queuing void cCmdEnQClump(CLUMP_Q * Queue, CLUMP_ID NewClump); void cCmdDeQClump(CLUMP_Q * Queue, CLUMP_ID Clump); void cCmdRotateQ(); UBYTE cCmdIsClumpOnQ(CLUMP_Q * Queue, CLUMP_ID Clump); UBYTE cCmdIsQSane(CLUMP_Q * Queue); // Rest queue functions NXT_STATUS cCmdSleepClump(ULONG time); UBYTE cCmdCheckRestQ(ULONG currTime); //Mutex queuing NXT_STATUS cCmdAcquireMutex(MUTEX_Q * Mutex); NXT_STATUS cCmdReleaseMutex(MUTEX_Q * Mutex); //Conditionally schedule dependents of given clump (Begin and End specify subset of list) NXT_STATUS cCmdSchedDependents(CLUMP_ID Clump, SWORD Begin, SWORD End); //Conditionally schedule TargetClump NXT_STATUS cCmdSchedDependent(CLUMP_ID Clump, CLUMP_ID TargetClump); //Test if ClumpID is sane at run-time (valid for indexing AllClumps) UBYTE cCmdIsClumpIDSane(CLUMP_ID Clump); // //Code stream management // //Instruction masking macros -- get the interesting bits out of an encoded instruction word #define COMP_CODE(pInstr) ((UBYTE)((((pInstr)[0]) & 0x0700) >> 8)) #define INSTR_SIZE(wd) ((wd) >> 12) & 0x0F; #define IS_SHORT_OP(pInstr) ((UBYTE)((((pInstr)[0]) & 0x0800) >> 8) == 8) #define SHORT_OP_CODE(pInstr) COMP_CODE(pInstr) #define SHORT_ARG(pInstr) ((SBYTE) (((pInstr)[0]) & 0x00FF)) //ShortOpMap defined in c_cmd_bytecodes.h #define OP_CODE(pInstr) (UBYTE) (((pInstr)[0]) & 0x00FF) // //Memory pool management // //Initialize entire memory pool with default value void cCmdInitPool(void); //Resize dataspace array specified by DSElementID and Offset. NXT_STATUS cCmdDSArrayAlloc(DS_ELEMENT_ID DSElementID, UWORD Offset, UWORD NewCount); //Resize dataspace array specified by DVIndex. In most cases, call higher-level cCmdDSArrayAlloc instead. NXT_STATUS cCmdDVArrayAlloc(DV_INDEX DVIndex, UWORD NewCount); NXT_STATUS cCmdAllocSubArrayDopeVectors(DS_ELEMENT_ID DSElementID, UWORD Offset); NXT_STATUS cCmdFreeSubArrayDopeVectors(DS_ELEMENT_ID DSElementID, UWORD Offset); NXT_STATUS cCmdAllocDopeVector(DV_INDEX *pIndex, UWORD ElemSize); NXT_STATUS cCmdFreeDopeVector(DV_INDEX DVIndex); NXT_STATUS cCmdGrowDopeVectorArray(UWORD NewCount); UWORD cCmdCalcArrayElemSize(DS_ELEMENT_ID DSElementID); NXT_STATUS cCmdMemMgrMoveToTail(DV_INDEX DVIndex); NXT_STATUS cCmdMemMgrInsertAtTail(DV_INDEX DVIndex); //Utility function to check sanity of MemMgr data structure. Boolean result. UBYTE cCmdVerifyMemMgr(); NXT_STATUS cCmdDSCompact(void); // // Message Queue management // NXT_STATUS cCmdMessageWrite(UWORD QueueID, UBYTE * pData, UWORD Length); NXT_STATUS cCmdMessageRead(UWORD QueueID, UBYTE * pData, UWORD Length, UBYTE Remove); NXT_STATUS cCmdMessageGetSize(UWORD QueueID, UWORD * Size); // // Datalog Queue management // NXT_STATUS cCmdDatalogWrite(UBYTE * pData, UWORD Length); NXT_STATUS cCmdDatalogRead(UBYTE * pData, UWORD Length, UBYTE Remove); NXT_STATUS cCmdDatalogGetSize(UWORD * Size); // // Color Sensor // NXT_STATUS cCmdColorSensorRead (UBYTE Port, SWORD* SensorValue, UWORD* RawArray, UWORD* NormalizedArray, SWORD* ScaledArray, UBYTE* InvalidData); // //Dataspace management // #define IS_AGGREGATE_TYPE(TypeCode) ((TypeCode == TC_ARRAY) || (TypeCode == TC_CLUSTER)) // use carefully, only where tc will be a scalar int #define QUICK_UNSIGNED_TEST(TypeCode) ((TypeCode) & 0x1) #define IS_SIGNED_TYPE(TypeCode) (((TypeCode) == TC_SBYTE) || ((TypeCode) == TC_SWORD) || ((TypeCode) == TC_SLONG)) //!!!BDUGGAN add TC_FLOAT? //Test if DS_ELEMENT_ID is sane at run-time (valid for indexing DS TOC) UBYTE cCmdIsDSElementIDSane(DS_ELEMENT_ID Index); DS_ELEMENT_ID cCmdGetDataspaceCount(void); //Pointer accessors to resolve actual data locations in RAM void* cCmdDSPtr(DS_ELEMENT_ID DSElementID, UWORD Offset); void* cCmdDVPtr(DV_INDEX DVIndex); //Helper to walk the DSTOC to the next entry at the same aggregate nesting level as CurrID DS_ELEMENT_ID cCmdNextDSElement(DS_ELEMENT_ID CurrID); //Recursively compare two complete data type descriptors UBYTE cCmdCompareDSType(DS_ELEMENT_ID DSElementID1, DS_ELEMENT_ID DSElementID2); //Functions for managing data flattened to byte arrays UWORD cCmdCalcFlattenedSize(DS_ELEMENT_ID DSElementID, UWORD Offset); NXT_STATUS cCmdFlattenToByteArray(UBYTE * pByteArray, UWORD * pByteOffset, DS_ELEMENT_ID DSElementID, UWORD Offset); NXT_STATUS cCmdUnflattenFromByteArray(UBYTE * pByteArray, UWORD * pByteOffset, DS_ELEMENT_ID DSElementID, UWORD Offset); //Comparison evaluation. Comparison codes defined in c_cmd_bytecodes.h. //cCmdCompare operates on scalars passed as ULONGs -- type-specific comparisons done inside function. UBYTE cCmdCompare(UBYTE CompCode, ULONG Val1, ULONG Val2, TYPE_CODE TypeCode1, TYPE_CODE TypeCode2); UBYTE cCmdCompareFlt(UBYTE CompCode, float Val1, float Val2, TYPE_CODE TypeCode1, TYPE_CODE TypeCode2); //cCmdCompareAggregates does polymorphic comparisons (with recursive helper function). NXT_STATUS cCmdCompareAggregates(UBYTE CompCode, UBYTE *ReturnBool, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3); NXT_STATUS cCmdRecursiveCompareAggregates(UBYTE CompCode, UBYTE *ReturnBool, UBYTE *Finished, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3); //Cluster functions UWORD cCmdClusterCount(DS_ELEMENT_ID DSElementID); //Array functions #define ARRAY_ELEM_OFFSET(DVIndex, Index) ((UWORD)(DV_ARRAY[(DVIndex)].Offset + DV_ARRAY[(DVIndex)].ElemSize * (Index))) UWORD cCmdGetDVIndex(DS_ELEMENT_ID DSElementID, UWORD Offset); UWORD cCmdArrayCount(DS_ELEMENT_ID DSElementID, UWORD Offset); TYPE_CODE cCmdArrayType(DS_ELEMENT_ID DSElementID); //!!! DATA_ARG masks are for internal use only! (Bytecode programs should never contain them) // See cCmdResolveDataArg() calls in the interpreter code for OP_GETOUT, OP_SETIN, and OP_GETIN. #define DATA_ARG_ADDR_MASK 0x3FFF #define DATA_ARG_IMM_MASK 0x7FFF //General data accessors (DS and IO Map) void * cCmdResolveDataArg(DATA_ARG DataArg, UWORD Offset, TYPE_CODE * TypeCode); void * cCmdResolveIODataArg(DATA_ARG DataArg, ULONG Offset, TYPE_CODE * TypeCode); ULONG cCmdGetVal(void * pVal, TYPE_CODE TypeCode); void cCmdSetVal(void * pVal, TYPE_CODE TypeCode, ULONG NewVal); // Calibration routines void cCmdLoadCalibrationFiles(void); NXT_STATUS cCmdComputeCalibratedValue(UBYTE *nm, SWORD *raw); void cCmdUpdateCalibrationCache(UBYTE *nm, SWORD min, SWORD max); // //Interpreter functions // //Clump-based "master" interpreter NXT_STATUS cCmdInterpFromClump(); //Function pointer typedef for sub-interpreters typedef NXT_STATUS (*pInterp)(CODE_WORD * const); typedef NXT_STATUS (*pInterpShort)(CODE_WORD * const); //Sub-interpreter dispatch functions NXT_STATUS cCmdInterpNoArg(CODE_WORD * const pCode); NXT_STATUS cCmdInterpUnop1(CODE_WORD * const pCode); NXT_STATUS cCmdInterpUnop2(CODE_WORD * const pCode); NXT_STATUS cCmdInterpScalarUnop2(CODE_WORD * const pCode); NXT_STATUS cCmdInterpBinop(CODE_WORD * const pCode); NXT_STATUS cCmdInterpScalarBinop(CODE_WORD * const pCode); NXT_STATUS cCmdInterpOther(CODE_WORD * const pCode); NXT_STATUS cCmdInterpShortError(CODE_WORD * const pCode); NXT_STATUS cCmdInterpShortSubCall(CODE_WORD * const pCode); NXT_STATUS cCmdInterpShortMove(CODE_WORD * const pCode); NXT_STATUS cCmdInterpShortAcquire(CODE_WORD * const pCode); NXT_STATUS cCmdInterpShortRelease(CODE_WORD * const pCode); NXT_STATUS cCmdMove(DATA_ARG Arg1, DATA_ARG Arg2); //Polymorphic interpreter functions NXT_STATUS cCmdInterpPolyUnop2(CODE_WORD const Code, DATA_ARG Arg1, UWORD Offset1, DATA_ARG Arg2, UWORD Offset2); ULONG cCmdUnop2(CODE_WORD const Code, ULONG Operand, TYPE_CODE TypeCode); float cCmdUnop2Flt(CODE_WORD const Code, float Operand, TYPE_CODE TypeCode); NXT_STATUS cCmdInterpPolyBinop(CODE_WORD const Code, DATA_ARG Arg1, UWORD Offset1, DATA_ARG Arg2, UWORD Offset2, DATA_ARG Arg3, UWORD Offset3); ULONG cCmdBinop(CODE_WORD const Code, ULONG LeftOp, ULONG RightOp, TYPE_CODE LeftType, TYPE_CODE RightType); float cCmdBinopFlt(CODE_WORD const Code, float LeftOp, float RightOp, TYPE_CODE LeftType, TYPE_CODE RightType); void cCmdSetValFlt(void * pVal, TYPE_CODE TypeCode, float NewVal); float cCmdGetValFlt(void * pVal, TYPE_CODE TypeCode); // //Support functions for lowspeed (I2C devices, i.e. ultrasonic sensor) communications // NXT_STATUS cCmdLSCheckStatus(UBYTE Port); UBYTE cCmdLSCalcBytesReady(UBYTE Port); NXT_STATUS cCmdLSWrite(UBYTE Port, UBYTE BufLength, UBYTE *pBuf, UBYTE ResponseLength); NXT_STATUS cCmdLSRead(UBYTE Port, UBYTE BufLength, UBYTE * pBuf); // //Support for OP_SYSCALL // // //Each cCmdWrap funtion below implements one system call. //The OP_SYSCALL interpreter wrangles the argument vector, ArgV, // then calls the appropriate wrapper function according to the SysCallID. //Wrapper functions write directly back into the dataspace via ArgV. // #define MAX_CALL_ARGS 16 typedef NXT_STATUS (*pSysCall)(UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileOpenRead(UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileOpenWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileOpenAppend(UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileRead(UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileClose(UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileResolveHandle (UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileRename (UBYTE * ArgV[]); NXT_STATUS cCmdWrapFileDelete (UBYTE * ArgV[]); NXT_STATUS cCmdWrapSoundPlayFile(UBYTE * ArgV[]); NXT_STATUS cCmdWrapSoundPlayTone(UBYTE * ArgV[]); NXT_STATUS cCmdWrapSoundGetState(UBYTE * ArgV[]); NXT_STATUS cCmdWrapSoundSetState(UBYTE * ArgV[]); NXT_STATUS cCmdWrapDrawText(UBYTE * ArgV[]); NXT_STATUS cCmdWrapDrawPoint(UBYTE * ArgV[]); NXT_STATUS cCmdWrapDrawLine(UBYTE * ArgV[]); NXT_STATUS cCmdWrapDrawCircle(UBYTE * ArgV[]); NXT_STATUS cCmdWrapDrawRect(UBYTE * ArgV[]); NXT_STATUS cCmdWrapDrawPicture(UBYTE * ArgV[]); NXT_STATUS cCmdWrapSetScreenMode(UBYTE * ArgV[]); NXT_STATUS cCmdWrapReadButton(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommLSWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommLSRead(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommLSCheckStatus(UBYTE * ArgV[]); NXT_STATUS cCmdWrapRandomNumber(UBYTE * ArgV[]); NXT_STATUS cCmdWrapGetStartTick(UBYTE * ArgV[]); NXT_STATUS cCmdWrapMessageWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapMessageRead(UBYTE * ArgV[]); NXT_STATUS cCmdWrapDatalogWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommBTCheckStatus(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommBTWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommBTRead(UBYTE * ArgV[]); NXT_STATUS cCmdWrapKeepAlive(UBYTE * ArgV[]); NXT_STATUS cCmdWrapIOMapRead(UBYTE * ArgV[]); NXT_STATUS cCmdWrapIOMapWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapColorSensorRead (UBYTE * ArgV[]); NXT_STATUS cCmdWrapDatalogGetTimes(UBYTE * ArgV[]); NXT_STATUS cCmdWrapSetSleepTimeout(UBYTE * ArgV[]); NXT_STATUS cCmdWrapListFiles(UBYTE * ArgV[]); // Handlers for dynamically added syscalls NXT_STATUS cCmdWrapCommHSWrite(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommHSRead(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommHSCheckStatus(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommBTOnOff(UBYTE * ArgV[]); NXT_STATUS cCmdWrapCommBTConnection(UBYTE * ArgV[]); NXT_STATUS cCmdWrapReadSemData(UBYTE * ArgV[]); NXT_STATUS cCmdWrapWriteSemData(UBYTE * ArgV[]); NXT_STATUS cCmdWrapUpdateCalibCacheInfo(UBYTE * ArgV[]); NXT_STATUS cCmdWrapComputeCalibValue(UBYTE * ArgV[]); //Handler for remote control protocol packets -- called from comm module via IO map function pointer UWORD cCmdHandleRemoteCommands(UBYTE * pInBuf, UBYTE * pOutBuf, UBYTE * pLen); #ifdef SIM_NXT // // Helper functions to provide simulator library access to VM internals // SWORD cCmdGetCodeWord(CLUMP_ID Clump, CODE_INDEX Index); UBYTE * cCmdGetDataspace(UWORD *DataspaceSize); DOPE_VECTOR * cCmdGetDopeVectorPtr(void); ULONG cCmdGetPoolSize(void); MEM_MGR cCmdGetMemMgr(void); #endif #else //!ENABLE_VM //Placeholder VARSCMD for alternate implementation (see bottom of c_cmd.c for usage notes) typedef struct { UBYTE Tmp; } VARSCMD; #endif //ENABLE_VM #endif //C_CMD nxt-firmware-1.29.7/src/c_cmd.iom000066400000000000000000000156321466344546000166220ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date: 3-02-09 9:28 $ // // Filename $Workfile:: c_cmd.iom $ // // Version $Revision: 5 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_cmd. $ // // Platform C // #ifndef CCMD_IOM #define CCMD_IOM #include "modules.h" #define pMapCmd ((IOMAPCMD*)(pHeaders[ENTRY_CMD]->pIOMap)) // // Status/error codes for the VM internal code and bytecodes, loosely categorized // Positive values are used for non-error status codes; commonly used by bytecode handlers // to affect future execution. // Negative values are run-time errors, and the first group is considered "fatal" in that // program execution cannot continue when these errors are encountered. // #define STAT_MSG_EMPTY_MAILBOX 64 //0x40 Specified mailbox contains no new messages #define STAT_MSG_BUFFERWRAP 16 //0x10 Datalog buffer not being read fast enough #define STAT_COMM_PENDING 32 //0x20 Pending setup operation in progress #define TIMES_UP 6 //0x06 Return to let drivers run #define ROTATE_QUEUE 5 //0x05 Give a slice to another queue #define STOP_REQ 4 //0x04 Abort current program #define BREAKOUT_REQ 3 //0x03 Break multi-instruction interpreter loop; give I/O a chance to run #define CLUMP_SUSPEND 2 //0x02 Place clump in stasis; execute others until this one returns to RunQ #define CLUMP_DONE 1 //0x01 Finish and reset this clump; execute others until this one is rescheduled #define NO_ERR 0 //Fatal errors #define ERR_ARG -1 //0xFF Bad arguments #define ERR_INSTR -2 //0xFE Illegal bytecode instruction #define ERR_FILE -3 //0xFD Mal-formed file contents #define ERR_VER -4 //0xFC Version mismatch between firmware and compiler #define ERR_MEM -5 //0xFB Insufficient memory available #define ERR_BAD_PTR -6 //0xFA Someone passed us a bad pointer! //General errors #define ERR_INVALID_PORT -16 //0xF0 Bad input or output port specified #define ERR_INVALID_FIELD -17 //0xEF Attempted to access invalid field of a structure #define ERR_INVALID_QUEUE -18 //0xEE Illegal queue ID specified #define ERR_INVALID_SIZE -19 //0xED Illegal size specified #define ERR_NO_PROG -20 //0xEC No active program //Communications specific errors #define ERR_COMM_CHAN_NOT_READY -32 //0xE0 Specified channel/connection not configured or busy #define ERR_COMM_CHAN_INVALID -33 //0xDF Specified channel/connection is not valid #define ERR_COMM_BUFFER_FULL -34 //0xDE No room in comm buffer #define ERR_COMM_BUS_ERR -35 //0xDD Something went wrong on the communications bus //Remote control ("direct commands") errors #define ERR_RC_ILLEGAL_VAL -64 //0xC0 Data contains out-of-range values #define ERR_RC_BAD_PACKET -65 //0xBF Clearly insane packet #define ERR_RC_UNKNOWN_CMD -66 //0xBE Unknown command opcode #define ERR_RC_FAILED -67 //0xBD Request failed (i.e. specified file not found) //NB: Error codes -96 through -128 (0xA0 through 0x80) reserved for loader (file system) errors //This whole range isn't actually used by current loader code, but it's a reasonable range to reserve #define IS_ERR(Status) ((Status) < NO_ERR) //Errors are considered fatal if they are something we'd consider halting the VM for. #define IS_FATAL(Status) ((Status) < NO_ERR && (Status) >= ERR_BAD_PTR) //Direct command protocol opcodes //!!! These MUST be mutually exclusive with c_comm's protocol opcodes. // Since all of c_comm's protocol opcodes are above 0x80, we're safe for now. enum { RC_START_PROGRAM, RC_STOP_PROGRAM, RC_PLAY_SOUND_FILE, RC_PLAY_TONE, RC_SET_OUT_STATE, RC_SET_IN_MODE, RC_GET_OUT_STATE, RC_GET_IN_VALS, RC_RESET_IN_VAL, RC_MESSAGE_WRITE, RC_RESET_POSITION, RC_GET_BATT_LVL, RC_STOP_SOUND, RC_KEEP_ALIVE, RC_LS_GET_STATUS, RC_LS_WRITE, RC_LS_READ, RC_GET_CURR_PROGRAM, RC_GET_BUTTON_STATE, RC_MESSAGE_READ, RC_RESERVED1, RC_RESERVED2, RC_RESERVED3, RC_RESERVED4, RC_RESERVED5, RC_DATALOG_READ, RC_DATALOG_SET_TIMES, RC_BT_GET_CONTACT_COUNT, RC_BT_GET_CONTACT_NAME, RC_BT_GET_CONN_COUNT, RC_BT_GET_CONN_NAME, RC_SET_PROPERTY, RC_GET_PROPERTY, RC_UPDATE_RESET_COUNT, NUM_RC_OPCODES }; // selectors for RC Get and Set properties enum { RC_PROP_BTONOFF, RC_PROP_SOUND_LEVEL, RC_PROP_SLEEP_TIMEOUT }; // //Published status of last program to be activated //This value is published so outside parties (like the UI) can check if a program is running, //and if not, how the last program ended. Initial value is "PROG_OK". //PROG_OK: Last program finished normally. //PROG_RUNNING: Program currently running //PROG_ERROR: Last program ended because of an error //PROG_ABORT: Last program ended because of (user) abort // typedef enum { PROG_IDLE, PROG_OK, PROG_RUNNING, PROG_ERROR, PROG_ABORT, PROG_RESET } PROGRAM_STATUS; //Maximum size of memory pool, in bytes //!!! Code assumes this value is evenly divisible by 4! #define POOL_MAX_SIZE 32768 //Versioning information //Format string must exist verbatim in the header of a valid program file. //Also included in IOMAPCMD for remote identification of the VM #define VM_FORMAT_STRING "MindstormsNXT" //Size of format string above, plus version number packed in the last two bytes. #define VM_FORMAT_STRING_SIZE 16 //Current firmware version defined in c_loader.iom as FIRMWAREVERSION //This is the oldest compatible version in the same system #define VM_OLDEST_COMPATIBLE_VERSION 0x0004 // //IO Map for Command Module // pRCHandler: Function pointer to handler for remote control protocol // Tick: Latest value from 1 ms system timer //!!! Two offset values below are useful for external debugging. They are only valid after a program has started! // OffsetDS: Offset to the dataspace (inside MemoryPool); relative to first byte of IOMapCmd // OffsetDVA: Offset to the DopeVectorArray (inside MemoryPool); relative to first byte of IOMapCmd // ProgStatus: Published status of last program to be activated // Awake: Boolean is only true after initialization // ActivateFlag: Set this flag to notify cCmdCtrl to activate new file // DeactivateFlag: Set this flag to notify cCmdCtrl to deactivate current program // FileName[]: Fill in this buffer when using ActivateFlag // MemoryPool[]: Main memory pool for program data. // (Declared as ULONG for portable alignment; used internally via a byte pointer.) // typedef struct { UBYTE FormatString[VM_FORMAT_STRING_SIZE]; UWORD (*pRCHandler)(UBYTE *, UBYTE *, UBYTE *); ULONG Tick; UWORD OffsetDS; UWORD OffsetDVA; PROGRAM_STATUS ProgStatus; UBYTE Awake; UBYTE ActivateFlag; UBYTE DeactivateFlag; UBYTE FileName[FILENAME_LENGTH + 1]; ULONG MemoryPool[POOL_MAX_SIZE / 4]; ULONG SyncTime; ULONG SyncTick; } IOMAPCMD; #endif //CCMD_IOM nxt-firmware-1.29.7/src/c_cmd_bytecodes.h000066400000000000000000000064331466344546000203250ustar00rootroot00000000000000#ifndef C_CMD_BYTECODES #define C_CMD_BYTECODES // // opcode definitions // symbol, bits, arg format // #define OPCODE_COUNT 0x38 //Family: Math #define OP_ADD 0x00 // dest, src1, src2 #define OP_SUB 0x01 // dest, src1, src2 #define OP_NEG 0x02 // dest, src #define OP_MUL 0x03 // dest, src1, src2 #define OP_DIV 0x04 // dest, src1, src2 #define OP_MOD 0x05 // dest, src1, src2 //Family: Logic #define OP_AND 0x06 // dest, src1, src2 #define OP_OR 0x07 // dest, src1, src2 #define OP_XOR 0x08 // dest, src1, src2 #define OP_NOT 0x09 // dest, src //Family: Bit manipulation #define OP_CMNT 0x0A // dest, src #define OP_LSL 0x0B // dest, src #define OP_LSR 0x0C // dest, src #define OP_ASL 0x0D // dest, src #define OP_ASR 0x0E // dest, src #define OP_ROTL 0x0F // dest, src #define OP_ROTR 0x10 // dest, src //Family: Comparison #define OP_CMP 0x11 // dest, src1, src2 #define OP_TST 0x12 // dest, src #define OP_CMPSET 0x13 // dest, src, testsrc, testsrc #define OP_TSTSET 0x14 // dest, src, testsrc //Family: Array ops #define OP_INDEX 0x15 // dest, src, index #define OP_REPLACE 0x16 // dest, src, index, val #define OP_ARRSIZE 0x17 // dest, src #define OP_ARRBUILD 0x18 // instrsize, dest, src1, src2, #define OP_ARRSUBSET 0x19 // dest, src, index, length #define OP_ARRINIT 0x1A // dest, elem, length //Family: Memory ops #define OP_MOV 0x1B // dest, src #define OP_SET 0x1C // dest, imm //Family: String ops #define OP_FLATTEN 0x1D // dest, src #define OP_UNFLATTEN 0x1E // dest, err, src, type #define OP_NUMTOSTRING 0x1F // dest, src #define OP_STRINGTONUM 0x20 // dest, offsetpast, src, offset, default #define OP_STRCAT 0x21 // instrsize, dest, src1, src2, #define OP_STRSUBSET 0x22 // dest, src, index, length #define OP_STRTOBYTEARR 0x23 // dest, src #define OP_BYTEARRTOSTR 0x24 // dest, src //Family: Control flow #define OP_JMP 0x25 // offset #define OP_BRCMP 0x26 // offset, src1, src2 #define OP_BRTST 0x27 // offset, src #define OP_SYSCALL 0x28 // func, args #define OP_STOP 0x29 // stop? //Family: Clump scheduling #define OP_FINCLUMP 0x2A // start, end #define OP_FINCLUMPIMMED 0x2B // clumpID #define OP_ACQUIRE 0x2C // mutexID #define OP_RELEASE 0x2D // mutexID #define OP_SUBCALL 0x2E // subroutine, callerID #define OP_SUBRET 0x2F // callerID //Family: IO ops #define OP_SETIN 0x30 // src, port, propid #define OP_SETOUT 0x31 // src, port, propid #define OP_GETIN 0x32 // dest, port, propid #define OP_GETOUT 0x33 // dest, port, propid //Family: Timing #define OP_WAIT 0x34 // dest, src #define OP_GETTICK 0x35 // dest //Family: Math NEW #define OP_SQRT 0x36 // dest, src #define OP_ABS 0x37 // dest, src // condition code definitions #define OPCC1_LT 0x00 #define OPCC1_GT 0x01 #define OPCC1_LTEQ 0x02 #define OPCC1_GTEQ 0x03 #define OPCC1_EQ 0x04 #define OPCC1_NEQ 0x05 // // short op definitions // #define USE_SHORT_OPS #define SHORT_OP_MOV 0 #define SHORT_OP_ACQUIRE 1 #define SHORT_OP_RELEASE 2 #define SHORT_OP_SUBCALL 3 // // short op mapping table // static UBYTE ShortOpMap[4] = { OP_MOV, OP_ACQUIRE, OP_RELEASE, OP_SUBCALL }; #endif // C_CMD_BYTECODES nxt-firmware-1.29.7/src/c_cmd_drawing.inc000066400000000000000000000571331466344546000203240ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 8/26/05 4:17p $ // // Filename $Workfile:: c_cmd.c $ // // Version $Revision:: 35 $ // // Archive $Archive:: /LMS2006/Sys01/Main/Firmware/Source/c_cmd_drawing.c $ // // Platform C // //absolute value of a #define ABS(a) (((a)<0) ? -(a) : (a)) //take binary sign of a, either -1, or 1 if >= 0 #define SGN(a) (((a)<0) ? -1 : 1) #define DISP_BUFFER_P ((UBYTE*)&(pMapDisplay->Normal)) //------------------------------------------------------------------ // cCmdClearScreenIfNeeded - Clear entire sceen buffer if explicitly requested or implicitly required. void cCmdClearScreenIfNeeded(ULONG DrawOptions); //------------------------------------------------------------------ // cCmdRestorDefaultScreen - Restore screen to default 'Running' screen void cCmdRestoreDefaultScreen(void); //------------------------------------------------------------------ // cCmdDrawString - Draw string to display buffer void cCmdDrawString(UBYTE *pString, ULONG X, ULONG Y); // OP codes supported by RIC files enum { IMG_DESCRIPTION_ID = 0, // Ignored at this time IMG_SPRITE_ID = 1, IMG_VARMAP_ID = 2, IMG_COPYBITS_ID = 3, IMG_PIXEL_ID = 4, IMG_LINE_ID = 5, IMG_RECTANGLE_ID = 6, IMG_CIRCLE_ID = 7, IMG_NUMBOX_ID = 8 }; #define IMG_SYMB_USEARGS(_v) (_v & (SWORD)0xF000) #define IMG_SYMB_MAP(_v) ((_v & 0x0F00) >> 8) #define IMG_SYMB_ARG(_v) (_v & 0x000F) // DrawingOptions #define DRAW_OPT_CLEAR_WHOLE_SCREEN (0x0001) #define DRAW_OPT_CLEAR_EXCEPT_STATUS_SCREEN (0x0002) #define DRAW_OPT_CLEAR_MODE(_v) ((_v) & 0x0003) // Clear Before Drawing Modes for Draw functions enum { DO_NOT_CLEAR = 0, CLEAR_B4_DRAW = 1 }; // Screen Modes for SetScreenMode function enum { RESTORE_NXT_SCREEN = 0 }; #define IMG_COMMON_FIELDS UWORD OpSize; UWORD OpCode; #define TRANSLATE_Y(_y) ((DISPLAY_HEIGHT-1) - (_y)) typedef struct { SWORD X, Y; } IMG_PT; typedef struct { IMG_PT Pt; SWORD Width, Height; } IMG_RECT; typedef struct { IMG_COMMON_FIELDS } IMG_OP_CORE; typedef struct { IMG_COMMON_FIELDS UWORD Options; UWORD Width; UWORD Height; } IMG_OP_DESCRIPTION; typedef struct { IMG_COMMON_FIELDS UWORD DataAddr; //Address sprite handle will be stored in. UWORD Rows; //Second deminsion of the array below. UWORD RowBytes; //The actual size of the following array. Must be even. UBYTE Bytes[2]; //Minimum of two for alignment purposes } IMG_OP_SPRITE; typedef struct { IMG_COMMON_FIELDS UWORD DataAddr; //Address sprite handle will be stored in. UWORD MapCount; //The actual size of the following array. Must be even. struct { //Minimum of two for alignment purposes UWORD Domain; UWORD Range; } MapElt[1]; } IMG_OP_VARMAP; typedef struct { IMG_COMMON_FIELDS UWORD CopyOptions; // Copy, CopyNot, Or, BitClear; UWORD DataAddr; // Address of an already defined sprite IMG_RECT Src; // Source rectangle IMG_PT Dst; // Destination left top } IMG_OP_COPYBITS; typedef struct { IMG_COMMON_FIELDS UWORD CopyOptions; IMG_PT Pt; UWORD Value; // typically mapped to an argument } IMG_OP_PIXEL; typedef struct { IMG_COMMON_FIELDS UWORD CopyOptions; IMG_PT Pt1; IMG_PT Pt2; } IMG_OP_LINE; typedef struct { IMG_COMMON_FIELDS UWORD CopyOptions; IMG_PT Pt; SWORD Width, Height; } IMG_OP_RECT; typedef struct { IMG_COMMON_FIELDS UWORD CopyOptions; IMG_PT Pt; UWORD Radius; } IMG_OP_CIRCLE; typedef struct { IMG_COMMON_FIELDS UWORD CopyOptions; IMG_PT Pt; UWORD Value; // typically mapped to an argument } IMG_OP_NUMBOX; typedef union { IMG_OP_CORE Core; IMG_OP_DESCRIPTION Desc; IMG_OP_SPRITE Sprite; IMG_OP_VARMAP VarMap; IMG_OP_COPYBITS CopyBits; IMG_OP_PIXEL Pixel; IMG_OP_LINE Line; IMG_OP_RECT Rect; IMG_OP_CIRCLE Circle; IMG_OP_NUMBOX NumBox; } IMG_OP_UNION; // Variables for DrawImage #define IMG_MAX_DATA 11 IMG_OP_UNION * gpImgData[IMG_MAX_DATA]; SLONG * gpPassedImgVars; SWORD gPassedVarsCount; // Private Prototypes void cCmdDrawLine(SLONG x1, SLONG y1, SLONG x2, SLONG y2); void cCmdDrawRect(SLONG left, SLONG bottom, SLONG width, SLONG hieght); void cCmdCopyBitMapBits(SLONG dst_x, SLONG dst_y, SLONG src_x, SLONG src_y, SLONG src_width, SLONG src_height, IMG_OP_SPRITE * pSprite); SLONG cCmdResolveValue(SWORD Value); void cCmdSetPixel(SLONG X, SLONG Y, ULONG Val); //----------------------------------------------------------------- //cCmdWrapDrawText //ArgV[0]: (Function return) Status byte, SBYTE //ArgV[1]: Location (IMG_PT *) //ArgV[2]: Text (CStr) //ArgV[3]: Options (ULONG) // NXT_STATUS cCmdWrapDrawText(UBYTE * ArgV[]) { IMG_PT * pPt = (IMG_PT*) ArgV[1]; ArgV[2] = (UBYTE*)cCmdDVPtr(*(DV_INDEX *)(ArgV[2])); //Resolve array argument cCmdClearScreenIfNeeded(*(ULONG*)ArgV[3]); // Display the String cCmdDrawString(ArgV[2], (UBYTE)pPt->X, (UBYTE)(pPt->Y)); pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); // Set return value *((SBYTE*)(ArgV[0])) = NO_ERR; return NO_ERR; } //----------------------------------------------------------------- //cCmdWrapDrawPoint //ArgV[0]: (Function return) Status byte, SBYTE //ArgV[1]: Location (IMG_PT *) //ArgV[2]: Options (ULONG) NXT_STATUS cCmdWrapDrawPoint(UBYTE * ArgV[]) { IMG_PT * pPt = (IMG_PT*) ArgV[1]; cCmdClearScreenIfNeeded(*(ULONG*)ArgV[2]); // Display the String cCmdSetPixel(pPt->X, pPt->Y, TRUE); pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); // Set return value *((SBYTE*)(ArgV[0])) = NO_ERR; return NO_ERR; } //----------------------------------------------------------------- //cCmdWrapDrawLine //ArgV[0]: (Function return) Status byte, SBYTE //ArgV[1]: Start Location (IMG_PT *) //ArgV[2]: End Location (IMG_PT *) //ArgV[3]: Options (ULONG) NXT_STATUS cCmdWrapDrawLine(UBYTE * ArgV[]) { IMG_PT * pPt1 = (IMG_PT*) ArgV[1]; IMG_PT * pPt2 = (IMG_PT*) ArgV[2]; cCmdClearScreenIfNeeded(*(ULONG*)ArgV[3]); cCmdDrawLine(pPt1->X, pPt1->Y, pPt2->X, pPt2->Y); pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); // Set return value *((SBYTE*)(ArgV[0])) = NO_ERR; return NO_ERR; } //----------------------------------------------------------------- //cCmdWrapDrawCircle //ArgV[0]: (Function return) Status byte, SBYTE //ArgV[1]: Start Location (IMG_PT *) //ArgV[2]: Radius (U8) //ArgV[3]: Options (ULONG) NXT_STATUS cCmdWrapDrawCircle(UBYTE * ArgV[]) { SLONG x, x1, y1, y, dp, delta; IMG_PT * pPt = (IMG_PT*) ArgV[1]; SLONG radius = *(UBYTE*)ArgV[2]; cCmdClearScreenIfNeeded(*(ULONG*)ArgV[3]); x1 = pPt->X; y1 = pPt->Y; x = 0; y = radius; dp=2*(1-radius); while(y >= 0) { cCmdSetPixel((x+x1), (y+y1), TRUE); cCmdSetPixel((-x+x1),(-y+y1), TRUE); cCmdSetPixel((x+x1), (-y+y1), TRUE); cCmdSetPixel((-x+x1),(y+y1), TRUE); if(dp<0) { delta = 2*dp + 2*y - 1; if (delta > 0) { x++; y--; dp += 2*x - 2*y + 2; } else { x++; dp += 2*x + 1; } } else if (dp > 0) { delta = 2*dp - 2*x - 1; if (delta > 0) { y--; dp += 1 - 2*y; } else { x++; y--; dp += 2*x - 2*y + 2; } } else { x++; y--; dp += 2*x - 2*y +2; } } pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); // Set return value *((SBYTE*)(ArgV[0])) = NO_ERR; return NO_ERR; } //----------------------------------------------------------------- //cCmdWrapDrawRect //ArgV[0]: (Function return) Status byte, SBYTE //ArgV[1]: TopLeft (IMG_PT *) //ArgV[2]: BottomRight (IMG_PT *) //ArgV[3]: Options (ULONG) NXT_STATUS cCmdWrapDrawRect(UBYTE * ArgV[]) { IMG_PT * pPt1 = (IMG_PT*) ArgV[1]; IMG_PT * pPt2 = (IMG_PT*) ArgV[2]; // Second point is actually (width, height) cCmdClearScreenIfNeeded(*(ULONG*)ArgV[3]); cCmdDrawRect(pPt1->X, pPt1->Y, pPt2->X, pPt2->Y); pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); // Set return value *((SBYTE*)(ArgV[0])) = NO_ERR; return NO_ERR; } //----------------------------------------------------------------- IMG_OP_UNION * cCmdGetIMGData(ULONG DataAddr) { if (DataAddr >= IMG_MAX_DATA) return NULL; else return gpImgData[DataAddr]; } //----------------------------------------------------------------- void cCmdSetIMGData(ULONG DataAddr, IMG_OP_UNION * pSprite) { if ((DataAddr >= 1) && (DataAddr < IMG_MAX_DATA)) gpImgData[DataAddr] = pSprite; } //----------------------------------------------------------------- SLONG cCmdResolveValue(SWORD Value) { if (!IMG_SYMB_USEARGS(Value)) { return Value; } else { IMG_OP_VARMAP * pVarMap; SLONG Arg; pVarMap = (IMG_OP_VARMAP *) cCmdGetIMGData((SWORD)IMG_SYMB_MAP(Value)); Arg = gpPassedImgVars[IMG_SYMB_ARG(Value)]; if (!pVarMap) { // No map, this implies a 1:1 mapping. return Arg; } else { // Scan through the list finding the pair the Arg lies between // Then linearly interpolate the mapping. SLONG i, DCur, RCur, DSpread, VSpread, RSpread; SLONG Count = pVarMap->MapCount; SLONG DPrev = pVarMap->MapElt[0].Domain; SLONG RPrev = pVarMap->MapElt[0].Range; if (Arg <= DPrev) { // Too small, map it to the first point return RPrev; } for (i = 1; i < Count; i++) { DCur = pVarMap->MapElt[i].Domain; RCur = pVarMap->MapElt[i].Range; if (Arg < DCur) { DSpread = DCur - DPrev; VSpread = Arg - DPrev; RSpread = RCur - RPrev; // Found the point and mapped, it return. return (RPrev+((VSpread*RSpread)/DSpread)); } DPrev = DCur; RPrev = RCur; } // If we get this far then it is too large, map it to the last point. return RCur; } } } //----------------------------------------------------------------- //cCmdWrapDrawGraphic //ArgV[0]: (Function return) Status Byte, SBYTE //ArgV[1]: Left Top (IMG_PT *) //ArgV[2]: Filename, CStr //ArgV[3]: Variables, array of I32 //ArgV[4]: Options (ULONG) NXT_STATUS cCmdWrapDrawPicture(UBYTE * ArgV[]) { SBYTE * pReturnVal = (SBYTE*)(ArgV[0]); LOADER_STATUS LStatus; NXT_STATUS DStatus = NO_ERR; ULONG DataSize; SLONG OpSize; IMG_PT Pt; // Where to draw the picture at (up and to the right) UBYTE ImageHandle; IMG_OP_UNION * pImage; //Resolve array argument ArgV[2] = (UBYTE*)cCmdDVPtr(*(DV_INDEX *)(ArgV[2])); ArgV[3] = (UBYTE*)cCmdDVPtr(*(DV_INDEX *)(ArgV[3])); cCmdClearScreenIfNeeded(*(ULONG*)ArgV[4]); //Open the file in memory map mode. return if failure. LStatus = pMapLoader->pFunc(OPENREADLINEAR, ArgV[2], (UBYTE*)(&pImage), &DataSize); ImageHandle = LOADER_HANDLE(LStatus); //If error opening file, give up and write loader status back to user. if (LOADER_ERR(LStatus) != SUCCESS || pImage == NULL) { *pReturnVal = (SBYTE)(LOADER_ERR_BYTE(LStatus)); return (NO_ERR); } //Else, start interpretting the file else { // Read the ArgV params, Clear the data table. Pt = *(IMG_PT*)ArgV[1]; //!!! Unsafe assumption that array is non-empty. Should check and avoid using pointer if empty. gpPassedImgVars = (SLONG*)ArgV[3]; memset(gpImgData,0,sizeof(gpImgData)); // Run through the op codes. while(!IS_ERR(DStatus)) { // Setup to look at an opcode, make sure it looke reasonable. if (DataSize < sizeof(IMG_OP_CORE)) { DStatus = ERR_FILE; break; // Too small to look at, somethings wrong. } OpSize = pImage->Core.OpSize + sizeof(UWORD); if (OpSize & 0x01) { DStatus = ERR_FILE; break; // Odd sizes not allowed. } switch(pImage->Core.OpCode) { case IMG_SPRITE_ID: { if (OpSize >= (SLONG)sizeof(IMG_OP_SPRITE)) cCmdSetIMGData(pImage->Sprite.DataAddr, pImage); } break; case IMG_VARMAP_ID: { if (OpSize >= (SLONG)sizeof(IMG_OP_VARMAP)) cCmdSetIMGData(pImage->VarMap.DataAddr, pImage); } break; case IMG_COPYBITS_ID: { if (OpSize >= (SLONG)sizeof(IMG_OP_COPYBITS)) { IMG_OP_COPYBITS * pCB = &(pImage->CopyBits); cCmdCopyBitMapBits( (cCmdResolveValue(pCB->Dst.X) + Pt.X), (cCmdResolveValue(pCB->Dst.Y) + Pt.Y), cCmdResolveValue((pCB->Src.Pt.X)), cCmdResolveValue((pCB->Src.Pt.Y)), cCmdResolveValue((pCB->Src.Width)), cCmdResolveValue((pCB->Src.Height)), (IMG_OP_SPRITE*)cCmdGetIMGData(cCmdResolveValue(pCB->DataAddr))); } } break; case IMG_LINE_ID: { if (OpSize >= (SLONG)sizeof(IMG_OP_LINE)) { IMG_OP_LINE * pL = &(pImage->Line); cCmdDrawLine( (cCmdResolveValue(pL->Pt1.X)+Pt.X), (cCmdResolveValue(pL->Pt1.Y)+Pt.Y), (cCmdResolveValue(pL->Pt2.X)+Pt.X), (cCmdResolveValue(pL->Pt2.Y)+Pt.Y) ); } } break; case IMG_RECTANGLE_ID: { if (OpSize >= (SLONG)sizeof(IMG_OP_LINE)) { IMG_OP_RECT * pL = &(pImage->Rect); cCmdDrawRect( (SWORD)(cCmdResolveValue(pL->Pt.X)+Pt.X), (SWORD)(cCmdResolveValue(pL->Pt.Y)+Pt.Y), (SWORD)(cCmdResolveValue(pL->Width)), (SWORD)(cCmdResolveValue(pL->Height)) ); } } break; case IMG_PIXEL_ID: { if (OpSize >= (SLONG)sizeof(IMG_OP_PIXEL)) { cCmdSetPixel( (cCmdResolveValue(pImage->Pixel.Pt.X) + Pt.X), (cCmdResolveValue(pImage->Pixel.Pt.Y) + Pt.Y), TRUE); } } break; case IMG_NUMBOX_ID: { if (OpSize >= (SLONG)sizeof(IMG_OP_NUMBOX)) { UBYTE NumStr[20]; IMG_OP_NUMBOX * pNB = &(pImage->NumBox); sprintf((PSZ)NumStr, "%d", cCmdResolveValue(pNB->Value)); cCmdDrawString( NumStr, (UBYTE) (cCmdResolveValue(pNB->Pt.X) + Pt.X), (UBYTE) (cCmdResolveValue(pNB->Pt.Y) + Pt.Y)); } } break; case IMG_DESCRIPTION_ID: { //No-op } break; default: { //Unrecognized opcode, pass an error back to the user. DStatus = ERR_FILE; } break; } DataSize -= OpSize; pImage = (IMG_OP_UNION*) ((UBYTE*)pImage + OpSize); } pMapDisplay->UpdateMask |= SCREEN_BIT(SCREEN_BACKGROUND); } // Set return value, close file and return *pReturnVal = DStatus; pMapLoader->pFunc(CLOSE, &ImageHandle, NULL, NULL); return (NO_ERR); } //----------------------------------------------------------------- // cCmdDrawLine - draw a line. All clipping is done by the set pixel function. void cCmdDrawLine( SLONG x1, SLONG y1, SLONG x2, SLONG y2) { SLONG d,x,y,ax,ay,sx,sy,dx,dy; // Initialize variables dx = x2-x1; ax = ABS(dx)<<1; sx = SGN(dx); dy = y2-y1; ay = ABS(dy)<<1; sy = SGN(dy); x = x1; y = y1; if (ax>ay) { /* x dominant */ d = ay-(ax>>1); for (;;) { cCmdSetPixel(x, y, TRUE); if (x==x2) return; if (d>=0) { y += sy; d -= ax; } x += sx; d += ay; } } else { /* y dominant */ d = ax-(ay>>1); for (;;) { cCmdSetPixel(x, y, TRUE); if (y==y2) return; if (d>=0) { x += sx; d -= ay; } y += sy; d += ax; } } } //----------------------------------------------------------------- // cCmdDrawRect - draw a rectangle. All clipping is done by the set pixel function. void cCmdDrawRect( SLONG left, SLONG bottom, SLONG width, SLONG height) { SLONG right = left + width; SLONG top = bottom + height; // Draw the four line segments cCmdDrawLine(left, top, right, top); cCmdDrawLine(right, top, right, bottom); cCmdDrawLine(right, bottom, left, bottom); cCmdDrawLine(left, bottom, left, top); } #ifndef DISPLAY_REALWIDTH #define DISPLAY_REALWIDTH DISPLAY_WIDTH #endif //----------------------------------------------------------------- //cCmdCopyBitMapBits void cCmdCopyBitMapBits( SLONG dst_x, // left pixel on LCD SLONG dst_y, // bottom pixel on LCD SLONG src_x, // starting pixel x coordinate from source map SLONG src_y, // starting pixel y coordinate from source map SLONG src_width, // width in pixels to the right (negative implies to the left) SLONG src_height, // height in pixels down (negative implies down) IMG_OP_SPRITE * pSprite) { SLONG dy; // Location in the destination pixmap , the screen that is SLONG sx; SLONG sy; // Location in the source pixmap. SLONG trim, last_x, last_y, rowbytes; UBYTE *pSrcByte; UBYTE *pDstBytes; UBYTE *pDstByte, *pFirstDstByte; UBYTE *pLastDstByte; UBYTE bit_y, not_bit_y; UBYTE masks[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; // Data in the image file is row major 8 pixels per byte.top row first. // src and dst coordinates treat the bottom left most pixel as (0,0) if (!pSprite || pSprite->OpCode!=IMG_SPRITE_ID) return; pDstBytes = DISP_BUFFER_P; // Clip the edges. Modify the source and width as well. if (dst_x < 0) { // bounds check start of x trim = (0 - dst_x); dst_x = 0; src_x += trim; src_width -= trim; } last_x = dst_x + src_width; if (last_x > DISPLAY_WIDTH) // bound check end of x last_x = DISPLAY_WIDTH; if (dst_y < 0) { // bound check start of y trim = (0 - dst_y); dst_y = 0; src_y += trim; // fix up source as well since we are clipping the start of the loop src_height -= trim; } last_y = dst_y + src_height; if (last_y > DISPLAY_HEIGHT) // bound check end of y last_y = DISPLAY_HEIGHT; // Convert the 0,0 bottom left origin to the top left 0,0 used by the actual // buffer last_y = TRANSLATE_Y(last_y); dst_y = TRANSLATE_Y(dst_y); // The last row is the top most scan line in the LCD Buffer // so limit if the copy would copy into memory before the buffer. // The first row copied will be the one closest to the bottom of the LCD // If that is off screen then limit as well and adjust the start point on the start // Copy bits top to top moving down. sy = src_y; rowbytes = pSprite->RowBytes; pSrcByte = pSprite->Bytes + ((pSprite->Rows - 1 - sy) * rowbytes); pFirstDstByte = pDstBytes + ((dst_y >> 3) * DISPLAY_REALWIDTH) + dst_x; for (dy = dst_y; dy > last_y; dy--) { sx = src_x; bit_y = masks[7 - (dy & 0x07)]; not_bit_y = ~ bit_y; pDstByte = pFirstDstByte; pLastDstByte = pDstByte + (last_x - dst_x); for (; pDstByte < pLastDstByte; pDstByte++) { if ( *(pSrcByte + (sx >> 3)) & masks[sx & 0x07] ){ *pDstByte |= bit_y; } else { *pDstByte &= not_bit_y; } sx ++; } pSrcByte -= rowbytes; sy ++; if ((dy & 0x07) == 0) // bump back the scan line start point at rollover pFirstDstByte -= DISPLAY_REALWIDTH; } } //----------------------------------------------------------------- // cCmdSetPixel - Set or clear a pixel based on Val void cCmdSetPixel(SLONG X, SLONG Y, ULONG Val) { Y = TRANSLATE_Y(Y); pMapDisplay->pFunc(DISPLAY_PIXEL, (UBYTE)Val, (UBYTE)X, (UBYTE)Y, 0, 0); } //----------------------------------------------------------------- //cCmdWrapSetScreenMode //ArgV[0]: (Function return) Status code, SBYTE //ArgV[1]: ScreenMode ULONG NXT_STATUS cCmdWrapSetScreenMode(UBYTE * ArgV[]) { ULONG ScreenMode = (ULONG)(*ArgV[1]); if (ScreenMode == RESTORE_NXT_SCREEN) { cCmdRestoreDefaultScreen(); } // Set return value *(SBYTE*)(ArgV[0]) = NO_ERR; return NO_ERR; } //------------------------------------------------------------------ // cCmdClearScreenIfNeeded - Clear entire sceen buffer if explicitly requested or implicitly required. void cCmdClearScreenIfNeeded(ULONG DrawOptions) { //If we are the first drawing command, clear the screen and record that we've done so if (VarsCmd.DirtyDisplay == FALSE) { VarsCmd.DirtyDisplay = TRUE; pMapUi->Flags &= ~UI_ENABLE_STATUS_UPDATE; //Override DrawOptions because we have to clear anyway DrawOptions = DRAW_OPT_CLEAR_WHOLE_SCREEN; } if (DRAW_OPT_CLEAR_MODE(DrawOptions)) { pMapDisplay->pFunc(DISPLAY_ERASE_ALL, 0, 0, 0, 0, 0); //Clear UpdateMask to kill any pending updates pMapDisplay->UpdateMask = 0; } return; } //------------------------------------------------------------------ // cCmdDrawString - Draw string to display buffer // Properly uses 'Normal' display buffer to avoid conflicts with popup buffer // Clips text at bottom and right hand edges of the screen buffer //!!! Function copied and modified from cDisplayString void cCmdDrawString(UBYTE *pString, ULONG X, ULONG Y) { UBYTE *pSource; UBYTE *pDestination; FONT *pFont; ULONG FontWidth; ULONG Items; ULONG Item; ULONG Line; //Get current font information pFont = pMapDisplay->pFont; Items = pFont->ItemsX * pFont->ItemsY; //Invert Y coordinate to match display buffer Y = TRANSLATE_Y(Y); Line = (Y & 0xF8) / 8; //If text line is out of bounds, do nothing. if (Line >= TEXTLINES) return; //Calculate pointer to first byte of drawing destination pDestination = &(DISP_BUFFER_P[Line * DISPLAY_WIDTH + X]); while (*pString) { FontWidth = pFont->ItemPixelsX; //Calculate X coordinate of the right edge of this character. //If it will extend past the right edge, clip the string. X += FontWidth; if (X >= DISPLAY_WIDTH) break; //If Item is defined by the font, display it. Else, ignore it. Item = *pString - ' '; if (Item < Items) { pSource = (UBYTE*)&(pFont->Data[Item * FontWidth]); while (FontWidth--) { *pDestination = *pSource; pDestination++; pSource++; } } pString++; } } //------------------------------------------------------------------ // cCmdRestoreDefaultScreen - Restore to Default 'Running' screen void cCmdRestoreDefaultScreen(void) { //If this program has taken over the display, reset it for the UI if (VarsCmd.DirtyDisplay == TRUE) { VarsCmd.DirtyDisplay = FALSE; pMapDisplay->pFunc(DISPLAY_ERASE_ALL, 0, 0, 0, 0, 0); pMapDisplay->UpdateMask = SCREEN_BIT(SCREEN_BACKGROUND); pMapUi->Flags |= UI_ENABLE_STATUS_UPDATE | UI_REDRAW_STATUS; } } nxt-firmware-1.29.7/src/c_comm.c000066400000000000000000003205431466344546000164500ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date: 8-09-08 14:11 $ // // Filename $Workfile:: c_comm.c $ // // Version $Revision: 7 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_comm $ // // Platform C // #include "stdconst.h" #include "modules.h" #include "c_comm.iom" #include "c_loader.iom" #include "c_ioctrl.iom" #include "c_ui.iom" #include "c_cmd.iom" #include "c_display.iom" #include "c_comm.h" #include "d_usb.h" #include "d_hispeed.h" #include "d_bt.h" #include #include #ifdef ARMDEBUG #include "debug_stub.h" #endif enum { DEVICE_VERIFIED, DEVICE_UPDATED, DEVICE_INSERTED }; #define DEFAULTBTADR "\x00\x16\x53\xFF\xFF\xFF" #define BTSTREAMTOUT 610 #define LOOKUPNO 3 #define CLEARExtMode {\ UBYTE Tmp;\ for(Tmp = 0; Tmp < NO_OF_CHANNELS; Tmp++)\ {\ VarsComm.ExtMode[Tmp].Status = FALSE;\ }\ } #define CHNumber(Bit) (Bit>>1) #define SETBtStateIdle VarsComm.ActiveUpdate = UPD_IDLE;\ VarsComm.UpdateState = 0;\ VarsComm.StreamStateCnt = 0;\ VarsComm.CmdSwitchCnt = 0;\ VarsComm.CloseConn0Cnt = 0;\ VarsComm.DiscAllCnt = 0;\ VarsComm.ResetBtCnt = 0 #define SETBtCmdState VarsComm.BtState = BT_ARM_CMD_MODE;\ IOMapComm.BtInBuf.InPtr = 0;\ CLEARExtMode;\ dBtClearArm7CmdSignal();\ dBtInitReceive(VarsComm.BtModuleInBuf.Buf, (UBYTE)CMD_MODE); #define SETBtDataState IOMapComm.BtInBuf.InPtr = 0;\ VarsComm.BtState = BT_ARM_DATA_MODE;\ dBtClearTimeOut(); /* stop cmd timeout because in datamode */\ dBtSetArm7CmdSignal();\ dBtInitReceive(VarsComm.BtModuleInBuf.Buf, (UBYTE)STREAM_MODE) #define SETBtOff VarsComm.BtState = BT_ARM_OFF;\ dBtSetBcResetPinLow() #define CLEARConnEntry(Index) memset((IOMapComm.BtConnectTable[Index].BdAddr), 0, SIZE_OF_BDADDR);\ memset(IOMapComm.BtConnectTable[Index].Name, 0, SIZE_OF_BT_NAME);\ memset((IOMapComm.BtConnectTable[Index].ClassOfDevice), 0, SIZE_OF_CLASS_OF_DEVICE);\ memset((IOMapComm.BtConnectTable[Index].PinCode), 0, SIZE_OF_BT_PINCODE);\ IOMapComm.BtConnectTable[Index].HandleNr = BLUETOOTH_HANDLE_UNDEFIEND;\ IOMapComm.BtConnectTable[Index].StreamStatus = 0;\ IOMapComm.BtConnectTable[Index].LinkQuality = 0 #define FILETXTOUT 30000 const UBYTE BootString[] = {"Let's dance: SAMBA"}; const UBYTE NoName[SIZE_OF_BT_NAME] = {"No Name"}; static IOMAPCOMM IOMapComm; static VARSCOMM VarsComm; static HEADER **pHeaders; const HEADER cComm = { 0x00050001L, "Comm", cCommInit, cCommCtrl, cCommExit, (void *)&IOMapComm, (void *)&VarsComm, (UWORD)sizeof(IOMapComm), (UWORD)sizeof(VarsComm), 0x0000 /* Code size - not used so far */ }; UWORD cCommReceivedBtData(void); void cCommBtCmdInterpreter(void); UWORD cCommInterprete(UBYTE *pInBuf, UBYTE *pOutBuf, UBYTE *pLength, UBYTE CmdBit, UWORD MsgLength); UWORD cCommInterpreteCmd(UBYTE Cmd, UBYTE *pInBuf, UBYTE *pOutBuf, UBYTE *pLength, UBYTE CmdBit, UWORD MsgLength); void cCommCpyToUpper(UBYTE *pDst, UBYTE *pSrc, UBYTE Length); void cCommCopyFileName(UBYTE *pDst, UBYTE *pSrc); void cCommSendHiSpeedData(void); void cCommReceivedHiSpeedData(void); UWORD cCommReq(UBYTE Cmd, UBYTE Param1, UBYTE Param2, UBYTE Param3, UBYTE *pName, UWORD *pRetVal); UBYTE cCommBtValidateCmd(void); void cCommClearStreamStatus(void); void cCommUpdateBt(void); UWORD cCommCopyBdaddr(UBYTE *pDst, UBYTE *pSrc); UWORD cCommInsertBtName(UBYTE *pDst, UBYTE *pSrc); UWORD cCommCheckBdaddr(UBYTE *pAdr, UBYTE *pSrc); UWORD cCommInsertDevice(UBYTE *pBdaddr, UBYTE *pName, UBYTE *pCod, UBYTE DeviceStatus, UBYTE *pAddInfo); void cCommsSetCmdMode(UBYTE *pNextState); void cCommsOpenStream(UBYTE *pNextState); void cCommsCloseConn0(UBYTE *pNextState); void cCommsDisconnectAll(UBYTE *pNextState); void cCommsBtReset(UBYTE *pNextState); void cCommPinCode(UBYTE *pPinCode); void cCommClrConnTable(void); SBYTE cCommSearchBTDevTableForName(UBYTE*); void cCommInit(void* pHeader) { UBYTE Tmp; pHeaders = pHeader; IOMapComm.pFunc = &cCommReq; IOMapComm.pFunc2 = &cCommPinCode; IOMapComm.UsbState = FALSE; IOMapComm.UsbOutBuf.OutPtr = 0; CLEARExtMode; dUsbInit(); dBtInit(); SETBtStateIdle; VarsComm.BtModuleInBuf.InPtr = 0; VarsComm.BtWaitTimeCnt = 0; /* Force a reset sequence on the BC4 */ VarsComm.pRetVal = &(VarsComm.RetVal); VarsComm.ActiveUpdate = UPD_RESET; VarsComm.BtState = BT_ARM_CMD_MODE; IOMapComm.BrickData.BtHwStatus = BT_DISABLE; for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { IOMapComm.BtDeviceTable[Tmp].DeviceStatus = BT_DEVICE_EMPTY; } IOMapComm.BtDeviceCnt = 0; IOMapComm.BrickData.BtStateStatus = 0; cCommClrConnTable(); dBtInitReceive(VarsComm.BtModuleInBuf.Buf, (UBYTE)CMD_MODE); dBtStartADConverter(); dHiSpeedInit(); VarsComm.HsState = 0; IOMapComm.UsbPollBuf.InPtr = 0; IOMapComm.UsbPollBuf.OutPtr = 0; VarsComm.BtAdrStatus = COLDBOOT; } void cCommCtrl(void) { if (FALSE == cCommReceivedBtData()) { /* there has been a timeout on the BC4 channel */ SETBtStateIdle; *(VarsComm.pRetVal) = BTTIMEOUT; if (COLDBOOT == VarsComm.BtAdrStatus) { /* there is an BT fatal error - set default bt adr and name*/ strcpy((char*)IOMapComm.BrickData.Name, (char*)UI_NAME_DEFAULT); dUsbStoreBtAddress((UBYTE*)DEFAULTBTADR); pMapUi->Flags |= UI_REDRAW_STATUS; dBtSetBcResetPinLow(); VarsComm.BtAdrStatus = BTADRERROR; } } cCommUpdateBt(); VarsComm.BtBcPinLevel = dBtGetBc4CmdSignal(); if (UPD_IDLE == VarsComm.ActiveUpdate) { switch (VarsComm.BtState) { /* Bluetooth device can either be in CMD, DATA or OFF state at top level */ case BT_ARM_OFF: { } break; case BT_ARM_CMD_MODE: { if (VarsComm.BtBcPinLevel) { SETBtDataState; } } break; case BT_ARM_DATA_MODE: { if (!(VarsComm.BtBcPinLevel)) { SETBtCmdState; } } break; } } IOMapComm.BtInBuf.Buf[BT_CMD_BYTE] = 0; /* Here comes the the HIGHSPEED_PORT implementation */ if (IOMapComm.HsFlags & HS_UPDATE) { IOMapComm.HsFlags &= ~HS_UPDATE; switch (IOMapComm.HsState) { case HS_INITIALISE: { dHiSpeedSetupUart(); IOMapComm.HsState = HS_INIT_RECEIVER; IOMapComm.HsFlags |= HS_UPDATE; } break; case HS_INIT_RECEIVER: { dHiSpeedInitReceive(VarsComm.HsModuleInBuf.Buf); VarsComm.HsState = 0x01; } break; case HS_SEND_DATA: { cCommSendHiSpeedData(); } break; case HS_DISABLE: { VarsComm.HsState = 0x00; dHiSpeedExit(); } break; } } if (VarsComm.HsState != 0) { cCommReceivedHiSpeedData(); } /* Here comes the the USB implementation */ ULONG Length; UWORD Status; if (0 != IOMapComm.UsbOutBuf.OutPtr) { dUsbWrite((const UBYTE *)IOMapComm.UsbOutBuf.Buf, (ULONG)IOMapComm.UsbOutBuf.OutPtr); IOMapComm.UsbOutBuf.OutPtr = 0; } Length = 0; if (TRUE == dUsbCheckConnection()) { pMapUi->UsbState = 1; if (TRUE == dUsbIsConfigured()) { Length = dUsbRead(IOMapComm.UsbInBuf.Buf, sizeof(IOMapComm.UsbInBuf.Buf)); IOMapComm.UsbState = TRUE; pMapUi->UsbState = 2; } } else { pMapUi->UsbState = 0; dUsbResetConfig(); if (TRUE == IOMapComm.UsbState) { IOMapComm.UsbState = FALSE; Status = dUsbGetFirstHandle(); while(0 == LOADER_ERR(Status)) { IOMapComm.UsbInBuf.Buf[0] = LOADER_HANDLE(Status); pMapLoader->pFunc(CLOSE, &(IOMapComm.UsbInBuf.Buf[0]), &(IOMapComm.UsbInBuf.Buf[2]), &Length); dUsbRemoveHandle(IOMapComm.UsbInBuf.Buf[0]); Status = dUsbGetNextHandle(); } } } if (0 != Length) { cCommInterprete(IOMapComm.UsbInBuf.Buf, IOMapComm.UsbOutBuf.Buf, (UBYTE*)&Length, USB_CMD_READY, (UWORD)Length); if (Length) { dUsbWrite((const UBYTE *)IOMapComm.UsbOutBuf.Buf, Length); } } dBtStartADConverter(); } void cCommExit(void) { dUsbExit(); dHiSpeedExit(); dBtExit(); } UBYTE cCommCheckSysFileType(UBYTE *pName) { UBYTE RtnVal; UBYTE TmpFilename[FILENAME_LENGTH + 1]; RtnVal = FALSE; cCommCpyToUpper(TmpFilename, &pName[1], (UBYTE)(FILENAME_LENGTH + 1)); if ((0 != strstr((PSZ)(TmpFilename), ".RXE")) || (0 != strstr((PSZ)(TmpFilename), ".SYS")) || (0 != strstr((PSZ)(TmpFilename), ".RTM"))) { RtnVal = TRUE; } return(RtnVal); } UWORD cCommInterprete(UBYTE *pInBuf, UBYTE *pOutBuf, UBYTE *pLength, UBYTE CmdBit, UWORD MsgLength) { UWORD ReturnStatus; UBYTE Channel; Channel = CHNumber(CmdBit); if (FALSE == VarsComm.ExtMode[Channel].Status) { switch (((pInBuf[0]) & ~NO_REPLY_BIT)) { case SYSTEM_CMD: { ReturnStatus = cCommInterpreteCmd(pInBuf[1], &(pInBuf[1]), &(pOutBuf[2]), pLength, CmdBit, MsgLength); /* Check if reply is requested */ if ((pInBuf[0]) & NO_REPLY_BIT) { /* Sender has choosen no reply */ *pLength = 0; /* if extended mode then remember the reply bit */ VarsComm.ExtMode[Channel].Type |= NO_REPLY_BIT; } else { /* Check if receiver wants to reply */ if (*pLength) { (*pLength)+= 2; pOutBuf[0] = REPLY_CMD; pOutBuf[1] = pInBuf[1]; } } } break; case DIRECT_CMD: { /* Adjust length to account for cmd type byte */ (*pLength) -= 1; /* If no reply requested, pass NULL output buffer pointer and clear *pLength */ if ((pInBuf[0]) & NO_REPLY_BIT) { pMapCmd->pRCHandler(&(pInBuf[0]), NULL, pLength); } else { pMapCmd->pRCHandler(&(pInBuf[0]), &(pOutBuf[2]), pLength); if (*pLength) { (*pLength) += 2; pOutBuf[0] = REPLY_CMD; pOutBuf[1] = pInBuf[1]; } } } break; case REPLY_CMD: { /* If this is a reply to a direct command opcode, pRCHandler will handle it */ if (pInBuf[1] < NUM_RC_OPCODES) pMapCmd->pRCHandler(&(pInBuf[0]), NULL, pLength); /* No Reply ever required on REPLY_CMD messages */ *pLength = 0; } break; #ifdef ARMDEBUG case DEBUG_CMD: { ReturnStatus = cCommHandleDebug(&(pInBuf[0]), CmdBit, MsgLength); /* Pass everything (incl. message command byte) to function */ /* Check that Debug Command does not expect reply */ ReturnStatus = (0 == ((pInBuf[0]) & NO_REPLY_BIT)); *pLength = 0; } break; #endif default: { /* UNSUPPORTED - don't reply on these messages */ *pLength = 0; } break; } } else { switch (VarsComm.ExtMode[Channel].Type & ~NO_REPLY_BIT) { case SYSTEM_CMD: { ReturnStatus = cCommInterpreteCmd(VarsComm.ExtMode[Channel].Cmd, &(pInBuf[0]), &(pOutBuf[2]), pLength, CmdBit, MsgLength); if ((VarsComm.ExtMode[Channel].Type) & NO_REPLY_BIT) { /* Sender has choosen no reply */ *pLength = 0; } else { /* Check if receiver wants to reply */ if (*pLength) { (*pLength) += 2; pOutBuf[0] = REPLY_CMD; pOutBuf[1] = VarsComm.ExtMode[Channel].Cmd; } } } break; case DIRECT_CMD: { } break; case REPLY_CMD: { } break; default: { } break; } } return(ReturnStatus); } UWORD cCommInterpreteCmd(UBYTE Cmd, UBYTE *pInBuf, UBYTE *pOutBuf, UBYTE *pLength, UBYTE CmdBit, UWORD MsgLength) { ULONG FileLength; UWORD Status; UBYTE Channel; Channel = CHNumber(CmdBit); switch(Cmd) { case OPENWRITE: { FileLength = pInBuf[21]; FileLength += (ULONG)pInBuf[22] << 8; FileLength += (ULONG)pInBuf[23] << 16; FileLength += (ULONG)pInBuf[24] << 24; if(TRUE == cCommCheckSysFileType(&pInBuf[1])) { Status = pMapLoader->pFunc(OPENWRITELINEAR, &pInBuf[1], NULL, &FileLength); } else { Status = pMapLoader->pFunc(OPENWRITE, &pInBuf[1], NULL, &FileLength); } pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); *pLength = 2; if ((SUCCESS == LOADER_ERR(Status)) && (CmdBit & USB_CMD_READY)) { dUsbInsertHandle(LOADER_HANDLE(Status)); } } break; case WRITE: { if (FALSE == VarsComm.ExtMode[Channel].Status) { FileLength = *pLength - 3; Status = pMapLoader->pFunc(WRITE, &(pInBuf[1]), &(pInBuf[2]), &FileLength); pOutBuf[2] = (UBYTE)(FileLength); pOutBuf[3] = (UBYTE)(FileLength >> 8); if ((*pLength != MsgLength) && (MsgLength != 0)) { /* This is the beginnig of and extended write command*/ VarsComm.ExtMode[Channel].Cmd = WRITE; VarsComm.ExtMode[Channel].Type = SYSTEM_CMD; VarsComm.ExtMode[Channel].Status = TRUE; VarsComm.ExtMode[Channel].Handle = LOADER_HANDLE(Status); *pLength = 0; } else { /* Normal write */ pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); *pLength = 4; } } else { UWORD TmpLen; FileLength = *pLength; Status = pMapLoader->pFunc(WRITE, &(VarsComm.ExtMode[Channel].Handle), &(pInBuf[0]), &FileLength); TmpLen = pOutBuf[3]; TmpLen <<= 8; TmpLen |= pOutBuf[2]; TmpLen += FileLength; pOutBuf[2] = (UBYTE)(TmpLen); pOutBuf[3] = (UBYTE)(TmpLen >> 8); if (MsgLength) { /* Don't answer before complete message has been received */ *pLength = 0; } else { /* Complete msg has been received */ VarsComm.ExtMode[Channel].Status = FALSE; pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); *pLength = 4; /* Remember the 2 length bytes */ } } } break; case OPENWRITEDATA: { FileLength = pInBuf[21]; FileLength += (ULONG)pInBuf[22] << 8; FileLength += (ULONG)pInBuf[23] << 16; FileLength += (ULONG)pInBuf[24] << 24; if(TRUE == cCommCheckSysFileType(&pInBuf[1])) { Status = ILLEGALFILENAME; } else { Status = pMapLoader->pFunc(OPENWRITEDATA, &pInBuf[1], NULL, &FileLength); } pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); *pLength = 2; if ((SUCCESS == LOADER_ERR(Status)) && (CmdBit & USB_CMD_READY)) { dUsbInsertHandle(LOADER_HANDLE(Status)); } } break; case OPENAPPENDDATA: { Status = pMapLoader->pFunc(OPENAPPENDDATA, &pInBuf[1], NULL, &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); pOutBuf[2] = (UBYTE)FileLength; pOutBuf[3] = (UBYTE)(FileLength >> 8); pOutBuf[4] = (UBYTE)(FileLength >> 16); pOutBuf[5] = (UBYTE)(FileLength >> 24); *pLength = 6; if ((SUCCESS == LOADER_ERR(Status)) && (CmdBit & USB_CMD_READY)) { dUsbInsertHandle(LOADER_HANDLE(Status)); } } break; case CLOSE: { if (CmdBit & USB_CMD_READY) { dUsbRemoveHandle(pInBuf[1]); } Status = pMapLoader->pFunc(CLOSE, &(pInBuf[1]), NULL, &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); *pLength = 2; } break; case CROPDATAFILE: { Status = pMapLoader->pFunc(CROPDATAFILE, &(pInBuf[1]), NULL, &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); *pLength = 2; } break; case OPENREAD: { Status = pMapLoader->pFunc(OPENREAD, &pInBuf[1], NULL, &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); pOutBuf[2] = (UBYTE)FileLength; pOutBuf[3] = (UBYTE)(FileLength >> 8); pOutBuf[4] = (UBYTE)(FileLength >> 16); pOutBuf[5] = (UBYTE)(FileLength >> 24); *pLength = 6; if ((SUCCESS == LOADER_ERR(Status)) && (CmdBit & USB_CMD_READY)) { dUsbInsertHandle(LOADER_HANDLE(Status)); } } break; case READ: { ULONG Length; FileLength = pInBuf[3]; FileLength <<= 8; FileLength |= pInBuf[2]; Length = FileLength; /* Here test for channel - USB can only handle a 64 byte return (- wrapping )*/ if (CmdBit & USB_CMD_READY) { if (FileLength > (SIZE_OF_USBBUF - 6)) { /* Buffer cannot hold the requested data adjust to buffer size */ FileLength = (SIZE_OF_USBBUF - 6); } *pLength = FileLength + 4; Status = pMapLoader->pFunc(READ, &pInBuf[1], &pOutBuf[4], &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); pOutBuf[2] = (UBYTE)Length; pOutBuf[3] = (UBYTE)(Length >> 8); if (FileLength < Length) { /* End of file is detcted - add up with zeros to the requested msg length */ Length -= FileLength; memset(&(pOutBuf[(FileLength + 4)]),0x00,Length); } } else { /* This is a BT request - BT can handle large packets */ if (FileLength > (SIZE_OF_BTBUF - 6)) { /* Read length exceeds buffer length check for extended read back */ if (SUCCESS == cCommReq(EXTREAD, 0x00, 0x00, 0x00, NULL, &(VarsComm.RetVal))) { /* More data requested than buffer can hold .... go into extended mode */ VarsComm.ExtTx.RemMsgSize = FileLength; VarsComm.ExtTx.SrcHandle = pInBuf[1]; VarsComm.ExtTx.Cmd = READ; *pLength = 0; } else { /* We were not able to go into extended mode bt was busy */ /* for now do not try to reply as it is not possible to */ /* return the requested bytes */ *pLength = 0; } } else { *pLength = FileLength + 4; Status = pMapLoader->pFunc(READ, &pInBuf[1], &pOutBuf[4], &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); pOutBuf[2] = (UBYTE)Length; pOutBuf[3] = (UBYTE)(Length >> 8); if (FileLength < Length) { /* End of file is detcted - add up with zeros to the requested msg length */ Length -= FileLength; memset(&(pOutBuf[(FileLength + 4)]),0x00,Length); } } } } break; case DELETE: { Status = pMapLoader->pFunc(DELETE, &pInBuf[1], NULL, &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); cCommCopyFileName(&pOutBuf[1], &pInBuf[1]); *pLength = FILENAME_LENGTH + 1 + 1; /*Filemname + 0 terminator + error byte */ } break; case FINDFIRST: { Status = pMapLoader->pFunc(FINDFIRST, &(pInBuf[1]), &(pOutBuf[2]), &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); if (LOADER_ERR_BYTE(SUCCESS) == pOutBuf[0]) { pOutBuf[22] = (UBYTE)FileLength; pOutBuf[23] = (UBYTE)(FileLength >> 8); pOutBuf[24] = (UBYTE)(FileLength >> 16); pOutBuf[25] = (UBYTE)(FileLength >> 24); if (CmdBit & USB_CMD_READY) { dUsbInsertHandle(pOutBuf[1]); } } else { /* ERROR - Fill with zeros */ memset(&(pOutBuf[2]),0x00,24); } *pLength = 26; } break; case FINDNEXT: { Status = pMapLoader->pFunc(FINDNEXT, &(pInBuf[1]), &(pOutBuf[2]), &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); if (LOADER_ERR_BYTE(SUCCESS) == pOutBuf[0]) { pOutBuf[22] = (UBYTE)FileLength; pOutBuf[23] = (UBYTE)(FileLength >> 8); pOutBuf[24] = (UBYTE)(FileLength >> 16); pOutBuf[25] = (UBYTE)(FileLength >> 24); } else { /* ERROR - Fill with zeros */ memset(&(pOutBuf[2]),0x00,24); } *pLength = 26; } break; case OPENREADLINEAR: { /* For internal use only */ } break; case VERSIONS: { pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); pOutBuf[1] = (UBYTE)PROTOCOLVERSION; pOutBuf[2] = (UBYTE)(PROTOCOLVERSION>>8); pOutBuf[3] = (UBYTE)FIRMWAREVERSION; pOutBuf[4] = (UBYTE)(FIRMWAREVERSION>>8); *pLength = 5; } break; case OPENWRITELINEAR: { FileLength = pInBuf[21]; FileLength += (ULONG)pInBuf[22] << 8; FileLength += (ULONG)pInBuf[23] << 16; FileLength += (ULONG)pInBuf[24] << 24; Status = pMapLoader->pFunc(OPENWRITELINEAR, &pInBuf[1], NULL, &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = LOADER_HANDLE(Status); *pLength = 2; if ((SUCCESS == LOADER_ERR(Status)) && (CmdBit & USB_CMD_READY)) { dUsbInsertHandle(LOADER_HANDLE(Status)); } } break; case FINDFIRSTMODULE: { Status = pMapLoader->pFunc(FINDFIRSTMODULE, &pInBuf[1], &pOutBuf[2], &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = 0; if (LOADER_ERR_BYTE(SUCCESS) != pOutBuf[0]) { memset(&pOutBuf[2], 0x00, 30); } *pLength = 32; } break; case FINDNEXTMODULE: { Status = pMapLoader->pFunc(FINDNEXTMODULE, pInBuf, &pOutBuf[2], &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = 0; if (LOADER_ERR_BYTE(SUCCESS) != pOutBuf[0]) { memset(&pOutBuf[2], 0x00, 30); } *pLength = 32; } break; case CLOSEMODHANDLE: { Status = pMapLoader->pFunc(CLOSEMODHANDLE, NULL, NULL, NULL); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[1] = 0; *pLength = 2; } break; case IOMAPREAD: { ULONG ModuleId; UWORD Length; ModuleId = pInBuf[1]; ModuleId |= (ULONG)pInBuf[2] << 8; ModuleId |= (ULONG)pInBuf[3] << 16; ModuleId |= (ULONG)pInBuf[4] << 24; /* Transfer the Module id */ pOutBuf[1] = pInBuf[1]; pOutBuf[2] = pInBuf[2]; pOutBuf[3] = pInBuf[3]; pOutBuf[4] = pInBuf[4]; /* Transfer the offset into the iomap (pOutBuf[6] is intended...)*/ pOutBuf[5] = pInBuf[5]; pOutBuf[6] = pInBuf[6]; /* Get the read *pLength */ FileLength = pInBuf[8]; FileLength <<= 8; FileLength |= pInBuf[7]; if (CmdBit & USB_CMD_READY) { /* test for USB buffer overrun */ if (FileLength > (SIZE_OF_USBBUF - 9)) { FileLength = SIZE_OF_USBBUF - 9; } } else { /* test for BT buffer overrun */ if (FileLength > (SIZE_OF_BTBUF - 9)) { FileLength = SIZE_OF_BTBUF - 9; } } Length = FileLength; *pLength = Length + 7; Status = pMapLoader->pFunc(IOMAPREAD, (UBYTE *)&ModuleId, &pOutBuf[5], &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[5] = (UBYTE)FileLength; pOutBuf[6] = (UBYTE)(FileLength >> 8); if (Length > FileLength) { Length -= FileLength; memset(&(pOutBuf[FileLength + 7]), 0x00, Length); } } break; case IOMAPWRITE: { ULONG ModuleId; pOutBuf[1] = pInBuf[1]; pOutBuf[2] = pInBuf[2]; pOutBuf[3] = pInBuf[3]; pOutBuf[4] = pInBuf[4]; ModuleId = pInBuf[1]; ModuleId |= (ULONG)pInBuf[2] << 8; ModuleId |= (ULONG)pInBuf[3] << 16; ModuleId |= (ULONG)pInBuf[4] << 24; FileLength = pInBuf[8]; FileLength <<= 8; FileLength |= pInBuf[7]; /* Place offset right before data */ pInBuf[8] = pInBuf[6]; pInBuf[7] = pInBuf[5]; Status = pMapLoader->pFunc(IOMAPWRITE, (UBYTE *)&ModuleId, &pInBuf[7], &FileLength); pOutBuf[0] = LOADER_ERR_BYTE(Status); pOutBuf[5] = (UBYTE)FileLength; pOutBuf[6] = (UBYTE)(FileLength >> 8); *pLength = 7; } break; case BOOTCMD: { UBYTE Tmp; /* The boot command is only allowed by USB - as firmware download can ONLY */ /* be send by USB */ pOutBuf[0] = LOADER_ERR_BYTE(UNDEFINEDERROR); memset(&(pOutBuf[1]), 0, 4); *pLength = 5; if (CmdBit & USB_CMD_READY) { Tmp = 0; while((Tmp < (sizeof(BootString) - 1)) && (BootString[Tmp] == pInBuf[Tmp+1])) { Tmp++; } if (Tmp == (sizeof(BootString) - 1)) { /* Yes valid boot sequence */ pMapDisplay->Flags &= ~DISPLAY_ON; pMapDisplay->Flags |= DISPLAY_REFRESH; pMapIoCtrl->PowerOn = BOOT; pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); pOutBuf[1] = 'Y'; pOutBuf[2] = 'e'; pOutBuf[3] = 's'; pOutBuf[4] = '\0'; } } } break; case SETBRICKNAME: { UWORD RtnVal; *pLength = 1; /* Update the name in the BT device - reply for this command is send */ /* before command is actually executed */ if (SUCCESS == cCommReq(SETBTNAME, 0, 0, 0, &pInBuf[1], &RtnVal)) { pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); cCommInsertBtName(IOMapComm.BrickData.Name, &pInBuf[1]); pMapUi->Flags |= UI_REDRAW_STATUS; } else { pOutBuf[0] = LOADER_ERR_BYTE(BTBUSY); } } break; case BTGETADR: { UBYTE Tmp; UBYTE *pAdr; pAdr = (IOMapComm.BrickData.BdAddr); for (Tmp = 0; Tmp < 7; Tmp++) { pOutBuf[Tmp + 1] = pAdr[Tmp]; } pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); *pLength = 8; } break; case DEVICEINFO: { pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); /* Brick name */ memcpy(&(pOutBuf[1]), IOMapComm.BrickData.Name, 15); /* BT address */ cCommCopyBdaddr(&(pOutBuf[16]), (IOMapComm.BrickData.BdAddr)); /* Link quality of the 4 possible connected devices */ pOutBuf[23] = IOMapComm.BtConnectTable[0].LinkQuality; pOutBuf[24] = IOMapComm.BtConnectTable[1].LinkQuality; pOutBuf[25] = IOMapComm.BtConnectTable[2].LinkQuality; pOutBuf[26] = IOMapComm.BtConnectTable[3].LinkQuality; /* Free user flash */ memcpy(&(pOutBuf[27]), &(pMapLoader->FreeUserFlash), sizeof(pMapLoader->FreeUserFlash)); /* Set answer length */ *pLength = 31; } break; case DELETEUSERFLASH: { Status = pMapLoader->pFunc(DELETEUSERFLASH, NULL, NULL, NULL); pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); *pLength = 1; } break; case POLLCMDLEN: { pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); pOutBuf[1] = pInBuf[1]; /* This is the Buf Number */ if (0 == pInBuf[1]) { /* USB poll buffer */ pOutBuf[2] = ((IOMapComm.UsbPollBuf.InPtr - IOMapComm.UsbPollBuf.OutPtr) & (SIZE_OF_USBBUF - 1)); } else { /* HI speed poll buffer */ pOutBuf[2] = ((IOMapComm.HsInBuf.InPtr - IOMapComm.HsInBuf.OutPtr) & (SIZE_OF_HSBUF - 1)); } *pLength = 3; } break; case POLLCMD: { UBYTE Tmp; UBYTE MaxBufData; pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); pOutBuf[1] = pInBuf[1]; *pLength = pInBuf[2]; if (CmdBit & USB_CMD_READY) { MaxBufData = (SIZE_OF_USBDATA - 5); /* Substract wrapping */ } else { MaxBufData = (SIZE_OF_BTBUF - 7); /* Substract wrapping + length bytes for BT*/ } if (0x00 == pInBuf[1]) { /* Data from USB poll buffer are requested */ if (*pLength <= MaxBufData) { for (Tmp = 0; ((Tmp < (*pLength)) && (IOMapComm.UsbPollBuf.InPtr != IOMapComm.UsbPollBuf.OutPtr)); Tmp++) { pOutBuf[3 + Tmp] = IOMapComm.UsbPollBuf.Buf[IOMapComm.UsbPollBuf.OutPtr]; IOMapComm.UsbPollBuf.OutPtr = ((IOMapComm.UsbPollBuf.OutPtr) + 1) % SIZE_OF_USBBUF; } pOutBuf[2] = Tmp; /* if end of buffer has been reached fill up with zeros */ memset(&(pOutBuf[Tmp + 3]), 0x00, (*pLength - Tmp)); } else { /* if more data requested than possible to return */ pOutBuf[0] = LOADER_ERR_BYTE(UNDEFINEDERROR); pOutBuf[1] = pInBuf[1]; /* This is buffer number */ pOutBuf[2] = 0; /* no of bytes returned */ *pLength = 0; } } else { /* Data from hi speed buffer are requested */ if (*pLength <= MaxBufData) { for (Tmp = 0; ((Tmp < (*pLength)) && (IOMapComm.HsInBuf.InPtr != IOMapComm.HsInBuf.OutPtr)); Tmp++) { pOutBuf[3 + Tmp] = IOMapComm.HsInBuf.Buf[IOMapComm.HsInBuf.OutPtr]; IOMapComm.HsInBuf.OutPtr = ((IOMapComm.HsInBuf.OutPtr) + 1) % SIZE_OF_USBBUF; } pOutBuf[2] = Tmp; /* if end of buffer has been reached fill up with zeros */ memset(&(pOutBuf[Tmp + 3]), 0x00, (*pLength - Tmp)); } else { /* if more data requested than possible to return */ pOutBuf[0] = LOADER_ERR_BYTE(UNDEFINEDERROR); pOutBuf[1] = pInBuf[1]; /* This is buffer number */ pOutBuf[2] = 0; /* no of bytes returned */ *pLength = 0; } } (*pLength) += 3; /* Add 3 bytes for the status byte, length byte and Buf no */ } break; case BTFACTORYRESET: { UWORD RtnVal; if (CmdBit & USB_CMD_READY) { if (SUCCESS == cCommReq(FACTORYRESET, 0, 0, 0, NULL, &RtnVal)) { /* Request success */ pOutBuf[0] = LOADER_ERR_BYTE(SUCCESS); } else { /* BT request error */ pOutBuf[0] = LOADER_ERR_BYTE(UNDEFINEDERROR); } } else { /* Factory reset request cannot be done by bluetooth */ pOutBuf[0] = LOADER_ERR_BYTE(UNDEFINEDERROR); } *pLength = 1; } break; default: { } break; } return(Status); } UWORD cCommReceivedBtData(void) { UWORD NumberOfBytes; UWORD BytesToGo; UWORD RtnVal; RtnVal = dBtReceivedData(&NumberOfBytes, &BytesToGo); if (TRUE == RtnVal) { /* Everything is fine go on */ if (NumberOfBytes != 0) { /* Copy the bytes into the IOMapBuffer */ memcpy((IOMapComm.BtInBuf.Buf), (VarsComm.BtModuleInBuf.Buf), NumberOfBytes); if (VarsComm.BtState == BT_ARM_CMD_MODE) { /* Call the BC4 command interpreter */ cCommBtCmdInterpreter(); IOMapComm.BtInBuf.InPtr = 0; } else { /* ActiveUpdate has to be idle because BC4 can send stream data even if CMD */ /* mode has been requested - dont try to interprete the data */ /* VarsComm.CmdSwitchCnt != 0 if a transition to Cmd mode is in process */ if ((VarsComm.BtState == BT_ARM_DATA_MODE) && (0 == VarsComm.CmdSwitchCnt)) { /* Move the inptr ahead */ IOMapComm.BtInBuf.InPtr = NumberOfBytes; /* using the outbuf inptr in order to get the number of bytes in the return answer at the right place*/ IOMapComm.BtOutBuf.InPtr = NumberOfBytes; /* call the data stream interpreter */ cCommInterprete(IOMapComm.BtInBuf.Buf, IOMapComm.BtOutBuf.Buf, &(IOMapComm.BtOutBuf.InPtr), (UBYTE) BT_CMD_READY, BytesToGo); /* if there is a reply to be send then send it */ if (IOMapComm.BtOutBuf.InPtr) { dBtSendMsg(IOMapComm.BtOutBuf.Buf, IOMapComm.BtOutBuf.InPtr, IOMapComm.BtOutBuf.InPtr); IOMapComm.BtOutBuf.InPtr = 0; } } } } } return(RtnVal); } void cCommBtCmdInterpreter(void) { /* this function handles all bluecode commands that can be */ /* initiated from the outside, meaning from other devices */ if(cCommBtValidateCmd()) { switch (IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { case MSG_REQUEST_PIN_CODE: { /* Pass the pin request on to cCommReq it'l handle it */ cCommReq(PINREQ, 0x00, 0x00, 0x00, NULL, &(VarsComm.RetVal)); } break; case MSG_REQUEST_CONNECTION: { /* Connect request from the outside */ cCommReq(CONNECTREQ, 0x00, 0x00, 0x00, NULL, &(VarsComm.RetVal)); } break; case MSG_LIST_RESULT: { switch (IOMapComm.BtInBuf.Buf[2]) { case LR_SUCCESS: { } break; case LR_ENTRY_REMOVED: { } break; /* case LR_COULD_NOT_SAVE: case LR_STORE_IS_FULL: case LR_UNKOWN_ADDR: */ default: { pMapUi->Error = (UBYTE)IOMapComm.BtInBuf.Buf[2]; pMapUi->BluetoothState |= BT_ERROR_ATTENTION; } break; } } break; case MSG_CLOSE_CONNECTION_RESULT: { UBYTE ConnNo; for (ConnNo = 0; ConnNo < SIZE_OF_BT_CONNECT_TABLE; ConnNo++) { if (IOMapComm.BtConnectTable[ConnNo].HandleNr == IOMapComm.BtInBuf.Buf[3]) { IOMapComm.BrickData.BtStateStatus &= ~(BT_CONNECTION_0_ENABLE<BluetoothState &= ~BT_STATE_CONNECTED; } pMapUi->Flags |= UI_REDRAW_STATUS; } break; case MSG_PORT_OPEN_RESULT: { if (IOMapComm.BtInBuf.Buf[2] == 1) { IOMapComm.BtConnectTable[0].HandleNr = IOMapComm.BtInBuf.Buf[3]; IOMapComm.BrickData.BtStateStatus |= BT_BRICK_PORT_OPEN; } else { /* There was an error setting up the OpenPort command in BC4 */ IOMapComm.BtConnectTable[0].HandleNr = BLUETOOTH_HANDLE_UNDEFIEND; IOMapComm.BrickData.BtStateStatus &= ~BT_BRICK_PORT_OPEN; } } break; case MSG_CLOSE_PORT_RESULT: { if (IOMapComm.BtInBuf.Buf[2] == 1) { IOMapComm.BtConnectTable[0].HandleNr = BLUETOOTH_HANDLE_UNDEFIEND; IOMapComm.BrickData.BtStateStatus &= ~BT_BRICK_PORT_OPEN; } } break; case MSG_PIN_CODE_ACK: { pMapUi->BluetoothState &= ~BT_PIN_REQUEST; } break; case MSG_DISCOVERABLE_ACK: { if (VarsComm.BtCmdData.ParamOne == 1) { IOMapComm.BrickData.BtStateStatus |= BT_BRICK_VISIBILITY; pMapUi->BluetoothState |= BT_STATE_VISIBLE; } else { IOMapComm.BrickData.BtStateStatus &= ~BT_BRICK_VISIBILITY; pMapUi->BluetoothState &= ~BT_STATE_VISIBLE; } } break; case MSG_RESET_INDICATION: { if ((UPD_RESET != VarsComm.ActiveUpdate) && (UPD_BRICKNAME != VarsComm.ActiveUpdate) && (UPD_FACTORYRESET != VarsComm.ActiveUpdate)) { /* Not intended reset indication - restart the bluecore */ if (VarsComm.ActiveUpdate != UPD_IDLE) { /* Something was ongoing send error message */ *(VarsComm.pRetVal) = (UWORD)ERR_COMM_BUS_ERR; *(VarsComm.pRetVal) |= 0x8000; } SETBtStateIdle; VarsComm.pRetVal = &(VarsComm.RetVal); VarsComm.ActiveUpdate = UPD_RESET; } } break; } } else { /* Receive a message with wrong checkSum ! */ } } void cCommCpyToUpper(UBYTE *pDst, UBYTE *pSrc, UBYTE Length) { UBYTE Tmp; for(Tmp = 0; Tmp < Length; Tmp++) { pDst[Tmp] =(UBYTE)toupper((UWORD)pSrc[Tmp]); } /* The requried length has been copied - now fill with zeros */ for(Tmp = Length; Tmp < (FILENAME_LENGTH + 1); Tmp++) { pDst[Tmp] = '\0'; } } void cCommCopyFileName(UBYTE *pDst, UBYTE *pSrc) { UBYTE Tmp; for(Tmp = 0; Tmp < (FILENAME_LENGTH + 1); Tmp++, pDst++) { if ('\0' != *pSrc) { *pDst = *pSrc; pSrc++; } else { *pDst = '\0'; } } } void cCommSendHiSpeedData(void) { VarsComm.HsModuleOutBuf.OutPtr = 0; for (VarsComm.HsModuleOutBuf.InPtr = 0; VarsComm.HsModuleOutBuf.InPtr < IOMapComm.HsOutBuf.InPtr; VarsComm.HsModuleOutBuf.InPtr++) { VarsComm.HsModuleOutBuf.Buf[VarsComm.HsModuleOutBuf.InPtr] = IOMapComm.HsOutBuf.Buf[IOMapComm.HsOutBuf.OutPtr]; IOMapComm.HsOutBuf.OutPtr++; } dHiSpeedSendData(VarsComm.HsModuleOutBuf.Buf, (VarsComm.HsModuleOutBuf.InPtr - VarsComm.HsModuleOutBuf.OutPtr)); } void cCommReceivedHiSpeedData(void) { UWORD NumberOfBytes; UWORD Tmp; dHiSpeedReceivedData(&NumberOfBytes); if (NumberOfBytes != 0) { for (Tmp = 0; Tmp < NumberOfBytes; Tmp++) { IOMapComm.HsInBuf.Buf[IOMapComm.HsInBuf.InPtr] = VarsComm.HsModuleInBuf.Buf[Tmp]; IOMapComm.HsInBuf.InPtr++; if (IOMapComm.HsInBuf.InPtr > (SIZE_OF_HSBUF - 1)) { IOMapComm.HsInBuf.InPtr = 0; } VarsComm.HsModuleInBuf.Buf[Tmp] = 0; } /* Now new data is available from the HIGH SPEED port ! */ } } UBYTE cCommBtValidateCmd(void) { UWORD CheckSumTmp = 0; UBYTE Tmp, CheckSumHigh, CheckSumLow; for (Tmp = 0; Tmp < (IOMapComm.BtInBuf.Buf[0] - 1);Tmp++) { CheckSumTmp += IOMapComm.BtInBuf.Buf[Tmp]; } CheckSumTmp = (UWORD) (1 + (0xFFFF - CheckSumTmp)); CheckSumHigh = (UBYTE)((CheckSumTmp & 0xFF00)>>8); CheckSumLow = (UBYTE)(CheckSumTmp & 0x00FF); if ((CheckSumHigh == IOMapComm.BtInBuf.Buf[IOMapComm.BtInBuf.Buf[0] - 1]) && (CheckSumLow == IOMapComm.BtInBuf.Buf[IOMapComm.BtInBuf.Buf[0]])) { return(TRUE); } else { return(FALSE); } } void cCommClearStreamStatus(void) { IOMapComm.BtConnectTable[0].StreamStatus = 0; IOMapComm.BtConnectTable[1].StreamStatus = 0; IOMapComm.BtConnectTable[2].StreamStatus = 0; IOMapComm.BtConnectTable[3].StreamStatus = 0; } void cCommUpdateBt(void) { UBYTE Tmp, Tmp2, Handle; Tmp = 0; Tmp2 = 0; switch(VarsComm.ActiveUpdate) { case UPD_RESET: { switch(VarsComm.UpdateState) { case 0: { /* Setup Reset sequence */ pMapUi->BluetoothState = BT_STATE_OFF; VarsComm.UpdateState = 1; } break; case 1: { cCommsBtReset(&(VarsComm.UpdateState)); } break; case 2: { (VarsComm.UpdateState)++; dBtSendBtCmd((UBYTE)MSG_GET_LOCAL_ADDR, 0, 0, NULL, NULL, NULL, NULL); } break; case 3: { if (MSG_GET_LOCAL_ADDR_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { cCommCopyBdaddr((IOMapComm.BrickData.BdAddr), &(IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 1])); dUsbStoreBtAddress( &(IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 1])); dBtSendBtCmd((UBYTE)MSG_GET_FRIENDLY_NAME, 0, 0, NULL, NULL, NULL, NULL); VarsComm.BtAdrStatus = INITIALIZED; (VarsComm.UpdateState)++; } } break; case 4: { if (MSG_GET_FRIENDLY_NAME_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { memcpy(IOMapComm.BrickData.Name, &(IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 1]), SIZE_OF_BRICK_NAME); pMapUi->Flags |= UI_REDRAW_STATUS; IOMapComm.BtDeviceCnt = 0; IOMapComm.BtDeviceNameCnt = 0; dBtSendBtCmd((UBYTE)MSG_DUMP_LIST, 0, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } } break; case 5: { if (MSG_LIST_ITEM == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { cCommCopyBdaddr((IOMapComm.BtDeviceTable[IOMapComm.BtDeviceCnt].BdAddr), &(IOMapComm.BtInBuf.Buf[2])); cCommInsertBtName(IOMapComm.BtDeviceTable[IOMapComm.BtDeviceCnt].Name, &(IOMapComm.BtInBuf.Buf[9])); IOMapComm.BtDeviceTable[IOMapComm.BtDeviceCnt].DeviceStatus = BT_DEVICE_KNOWN; memcpy(IOMapComm.BtDeviceTable[IOMapComm.BtDeviceCnt].ClassOfDevice, &(IOMapComm.BtInBuf.Buf[9+SIZE_OF_BT_NAME]), sizeof(IOMapComm.BtDeviceTable[IOMapComm.BtDeviceCnt].ClassOfDevice)); IOMapComm.BtDeviceCnt++; IOMapComm.BtDeviceNameCnt++; } if (MSG_LIST_DUMP_STOPPED == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { dBtSendBtCmd((UBYTE)MSG_GET_VERSION, 0, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } IOMapComm.BtInBuf.Buf[BT_CMD_BYTE] = 0; } break; case 6: { if (MSG_GET_VERSION_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { IOMapComm.BrickData.BluecoreVersion[0] = IOMapComm.BtInBuf.Buf[3]; IOMapComm.BrickData.BluecoreVersion[1] = IOMapComm.BtInBuf.Buf[2]; /* BtHwStatus indicates cold boot or user interaction */ if (BT_DISABLE == IOMapComm.BrickData.BtHwStatus) { /* This is from brick turning on */ dBtSendBtCmd((UBYTE)MSG_GET_BRICK_STATUSBYTE, 0, 0, NULL, NULL, NULL, NULL); } else { /* this is user interaction setting the brick on */ dBtSendBtCmd((UBYTE)MSG_SET_BRICK_STATUSBYTE, BT_ENABLE, 0, NULL, NULL, NULL, NULL); } (VarsComm.UpdateState)++; pMapUi->Flags |= UI_REDRAW_STATUS; } } break; case 7: { if (MSG_GET_BRICK_STATUSBYTE_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { IOMapComm.BrickData.TimeOutValue = IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 2]; /* Check for brick to be on or off */ if (BT_ENABLE == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 1]) { pMapUi->BluetoothState &= ~BT_STATE_OFF; IOMapComm.BrickData.BtHwStatus = BT_ENABLE; dBtSendBtCmd((UBYTE)MSG_GET_DISCOVERABLE, 0, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } else { SETBtOff; IOMapComm.BrickData.BtHwStatus = BT_ENABLE; SETBtStateIdle; *(VarsComm.pRetVal) = SUCCESS; } } if (MSG_SET_BRICK_STATUSBYTE_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* brick to be on*/ pMapUi->BluetoothState &= ~BT_STATE_OFF; IOMapComm.BrickData.BtHwStatus = BT_ENABLE; dBtSendBtCmd((UBYTE)MSG_GET_DISCOVERABLE, 0, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } } break; case 8: { if (MSG_GET_DISCOVERABLE_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if (IOMapComm.BtInBuf.Buf[2] & 0x01) { IOMapComm.BrickData.BtStateStatus |= BT_BRICK_VISIBILITY; pMapUi->BluetoothState |= BT_STATE_VISIBLE; } else { IOMapComm.BrickData.BtStateStatus &= ~BT_BRICK_VISIBILITY; pMapUi->BluetoothState &= ~BT_STATE_VISIBLE; } dBtSendBtCmd((UBYTE)MSG_OPEN_PORT, 0, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } } break; case 9: { if (MSG_PORT_OPEN_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if (IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 1] & 0x01) { IOMapComm.BrickData.BtStateStatus |= BT_BRICK_PORT_OPEN; } else { IOMapComm.BrickData.BtStateStatus &= ~BT_BRICK_PORT_OPEN; } SETBtStateIdle; *(VarsComm.pRetVal) = SUCCESS; } } break; } } break; case UPD_FACTORYRESET: { switch(VarsComm.UpdateState) { case 0: { if (BT_STATE_OFF & (pMapUi->BluetoothState)) { /* Bluetooth is off - now start it up */ (VarsComm.UpdateState)++; } else { /* BT is already on - continue */ (VarsComm.UpdateState) += 2; } } break; case 1: { cCommsBtReset(&(VarsComm.UpdateState)); } break; case 2: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 3: { cCommsDisconnectAll(&(VarsComm.UpdateState)); } break; case 4: { /* Now bc4 is in cmd mode now factory can be sent */ /* Just leave the BC4 in cmd mode */ dBtSendBtCmd((UBYTE)MSG_SET_FACTORY_SETTINGS, 0, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } break; case 5: { if (MSG_SET_FACTORY_SETTINGS_ACK == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { SETBtStateIdle; IOMapComm.BrickData.BtHwStatus = BT_DISABLE; /* Boot BT like cold boot*/ VarsComm.ActiveUpdate = UPD_RESET; } } break; } } break; case UPD_BRICKNAME: { switch(VarsComm.UpdateState) { case 0: { if (BT_STATE_OFF & (pMapUi->BluetoothState)) { /* Bluetooth is off - now start it up */ (VarsComm.UpdateState)++; } else { VarsComm.UpdateState = 2; } } break; case 1: { cCommsBtReset(&(VarsComm.UpdateState)); } break; case 2: { VarsComm.BtUpdateDataConnectNr = 0; if (BT_ARM_DATA_MODE == VarsComm.BtState) { for (Tmp = 0; Tmp < SIZE_OF_BT_CONNECT_TABLE; Tmp++) { if (IOMapComm.BtConnectTable[Tmp].StreamStatus) { VarsComm.BtUpdateDataConnectNr = Tmp | 0x80; } } (VarsComm.UpdateState)++; } else { (VarsComm.UpdateState) += 2; } } break; case 3: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 4: { /* Brick name has been updated prior to this */ dBtSendBtCmd((UBYTE)MSG_SET_FRIENDLY_NAME, 0, 0, NULL, IOMapComm.BrickData.Name, NULL, NULL); (VarsComm.UpdateState)++; } break; case 5: { if (MSG_SET_FRIENDLY_NAME_ACK == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* Set name has been executed */ if (VarsComm.BtUpdateDataConnectNr & 0x80) { dBtSendBtCmd((UBYTE)MSG_OPEN_STREAM, IOMapComm.BtConnectTable[(VarsComm.BtUpdateDataConnectNr & ~0x80)].HandleNr, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } else { if (BT_STATE_OFF & (pMapUi->BluetoothState)) { SETBtOff; } SETBtStateIdle; *(VarsComm.pRetVal) = SUCCESS; } pMapUi->Flags |= UI_REDRAW_STATUS; } } break; case 6: { if (VarsComm.BtBcPinLevel) { IOMapComm.BtConnectTable[(VarsComm.BtUpdateDataConnectNr & ~0x80)].StreamStatus = 1; *(VarsComm.pRetVal) = SUCCESS; SETBtDataState; SETBtStateIdle; } } break; } } break; case UPD_REQCMDMODE: { switch(VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } break; } } break; case UPD_OPENSTREAM: { switch(VarsComm.UpdateState) { case 0: { cCommsOpenStream(&(VarsComm.UpdateState)); } break; case 1: { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } break; } } break; case UPD_SENDFILE: { switch (VarsComm.UpdateState) { case 0: { cCommsOpenStream(&(VarsComm.UpdateState)); } break; case 1: { /* Here we wait for the open stream to succeed*/ if (IOMapComm.BtConnectTable[VarsComm.ExtTx.SlotNo].StreamStatus) { /* Stream has been opened send the openwrite command */ VarsComm.BtModuleOutBuf.Buf[0] = SYSTEM_CMD; VarsComm.BtModuleOutBuf.Buf[1] = OPENWRITE; memcpy((UBYTE*)&(VarsComm.BtModuleOutBuf.Buf[2]),(UBYTE*)VarsComm.ExtTx.FileName, FILENAME_LENGTH + 1); memcpy((UBYTE*)&(VarsComm.BtModuleOutBuf.Buf[22]),(UBYTE*)&(VarsComm.ExtTx.RemFileSize), sizeof(VarsComm.ExtTx.RemFileSize)); dBtSendMsg(VarsComm.BtModuleOutBuf.Buf, 26, 26); VarsComm.ExtTx.Timer = 0; VarsComm.UpdateState = 2; } else { if (VarsComm.ExtTx.Timer >= FILETXTOUT) { *(VarsComm.pRetVal) = FILETX_STREAMERROR; VarsComm.UpdateState = 8; } else { (VarsComm.ExtTx.Timer)++; } } } break; case 2: { if (4 == IOMapComm.BtInBuf.InPtr) { /* Data has been received - examine the answer */ if ((REPLY_CMD == IOMapComm.BtInBuf.Buf[0]) && (OPENWRITE == IOMapComm.BtInBuf.Buf[1])) { /* OpenWrite answer */ if (LOADER_ERR_BYTE(SUCCESS) == IOMapComm.BtInBuf.Buf[2]) { /* save the handle from the other brick */ VarsComm.ExtTx.DstHandle = IOMapComm.BtInBuf.Buf[3]; VarsComm.UpdateState = 3; IOMapComm.BtInBuf.InPtr = 0; } else { /* Open write failiure - terminate file transfer */ *(VarsComm.pRetVal) = IOMapComm.BtInBuf.Buf[2]; VarsComm.UpdateState = 8; } } } if (VarsComm.ExtTx.Timer >= FILETXTOUT) { *(VarsComm.pRetVal) = FILETX_TIMEOUT; VarsComm.UpdateState = 8; } else { (VarsComm.ExtTx.Timer)++; } } break; case 3: /*SENDWRITE:*/ { ULONG Length; UWORD MsgSize; VarsComm.ExtTx.Timer = 0; if (VarsComm.ExtTx.RemFileSize > (MAX_BT_MSG_SIZE - 5)) { /* need to use the maximum size available - approx 64K */ VarsComm.ExtTx.RemMsgSize = (MAX_BT_MSG_SIZE - 5); } else { /* Message can hold the remaining message */ VarsComm.ExtTx.RemMsgSize = VarsComm.ExtTx.RemFileSize; } if (VarsComm.ExtTx.RemMsgSize > (SIZE_OF_BTBUF - 5)) { Length = SIZE_OF_BTBUF - 5; VarsComm.UpdateState = 4; } else { Length = VarsComm.ExtTx.RemMsgSize; VarsComm.UpdateState = 5; } Handle = (UBYTE)(VarsComm.ExtTx.SrcHandle); pMapLoader->pFunc(READ, &Handle, &(VarsComm.BtModuleOutBuf.Buf[3]), &Length); MsgSize = VarsComm.ExtTx.RemMsgSize + 3; VarsComm.BtModuleOutBuf.Buf[0] = SYSTEM_CMD; VarsComm.BtModuleOutBuf.Buf[1] = WRITE; VarsComm.BtModuleOutBuf.Buf[2] = VarsComm.ExtTx.DstHandle; dBtSendMsg(VarsComm.BtModuleOutBuf.Buf, Length + 3, MsgSize); VarsComm.ExtTx.RemMsgSize -= Length; VarsComm.ExtTx.RemFileSize -= Length; } break; case 4: /* CONTINOUSWRITE:*/ { ULONG Length; UWORD Status; if(dBtCheckForTxBuf()) { /* do only send more data if buffer is empty */ VarsComm.ExtTx.Timer = 0; if (VarsComm.ExtTx.RemMsgSize >= SIZE_OF_BTBUF) { Length = SIZE_OF_BTBUF; } else { Length = VarsComm.ExtTx.RemMsgSize; } VarsComm.ExtTx.RemMsgSize -= Length; VarsComm.ExtTx.RemFileSize -= Length; Handle = (UBYTE)(VarsComm.ExtTx.SrcHandle); Status = pMapLoader->pFunc(READ, &Handle, &(VarsComm.BtModuleOutBuf.Buf[0]), &Length); if (Status >= 0x8000) { Length = 0; } dBtSend(VarsComm.BtModuleOutBuf.Buf, Length); if (!(VarsComm.ExtTx.RemMsgSize)) { /* at this point due to large write command acknowledge is expected */ VarsComm.UpdateState = 5; VarsComm.ExtTx.Timer = 0; IOMapComm.BtInBuf.InPtr = 0; } } } break; case 5: /* WRITEACK: */ { if (6 == IOMapComm.BtInBuf.InPtr) { if ((WRITE == IOMapComm.BtInBuf.Buf[1]) && (REPLY_CMD == IOMapComm.BtInBuf.Buf[0]) && (VarsComm.ExtTx.DstHandle == IOMapComm.BtInBuf.Buf[3])) { /* Ok the the return reply is for me - was it ok? */ if (LOADER_ERR_BYTE(SUCCESS) == IOMapComm.BtInBuf.Buf[2]) { /* Ok send next write*/ if (VarsComm.ExtTx.RemFileSize) { VarsComm.UpdateState = 3; } else { VarsComm.UpdateState = 6; } IOMapComm.BtInBuf.InPtr = 0; } } } if (VarsComm.ExtTx.Timer >= FILETXTOUT) { *(VarsComm.pRetVal) = FILETX_TIMEOUT; VarsComm.UpdateState = 8; } else { (VarsComm.ExtTx.Timer)++; } } break; case 6: /*TERMINATESEND: */ { /* Stream still open close the receiver handle */ VarsComm.BtModuleOutBuf.Buf[0] = SYSTEM_CMD; VarsComm.BtModuleOutBuf.Buf[1] = CLOSE; VarsComm.BtModuleOutBuf.Buf[2] = VarsComm.ExtTx.DstHandle; dBtSendMsg(VarsComm.BtModuleOutBuf.Buf, 3, 3); VarsComm.ExtTx.Timer = 0; VarsComm.UpdateState = 7; } break; case 7: /* TERMINATEACK:*/ { if (4 == IOMapComm.BtInBuf.InPtr) { if ((CLOSE == IOMapComm.BtInBuf.Buf[1]) && (REPLY_CMD == IOMapComm.BtInBuf.Buf[0]) && (VarsComm.ExtTx.DstHandle == IOMapComm.BtInBuf.Buf[3])) { if (LOADER_ERR_BYTE(SUCCESS) == IOMapComm.BtInBuf.Buf[2]) { *(VarsComm.pRetVal) = SUCCESS; VarsComm.UpdateState = 8; } else { *(VarsComm.pRetVal) = FILETX_CLOSEERROR; VarsComm.UpdateState = 8; } IOMapComm.BtInBuf.InPtr = 0; } } if (VarsComm.ExtTx.Timer >= FILETXTOUT) { *(VarsComm.pRetVal) = FILETX_TIMEOUT; VarsComm.UpdateState = 8; } else { (VarsComm.ExtTx.Timer)++; } } break; case 8: { UBYTE Handle; Handle = (UBYTE)(VarsComm.ExtTx.SrcHandle); pMapLoader->pFunc(CLOSE, &Handle, NULL, NULL); (VarsComm.UpdateState)++; } break; case 9: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 10: { SETBtStateIdle; } break; } } break; case UPD_EXTREAD: { switch (VarsComm.UpdateState) { case 0: { ULONG MsgLength; UWORD Status; MsgLength = (SIZE_OF_BTBUF - 8); Handle =(UBYTE)(VarsComm.ExtTx.SrcHandle); Status = pMapLoader->pFunc(READ, &Handle, &(VarsComm.BtModuleOutBuf.Buf[6]), &MsgLength); VarsComm.BtModuleOutBuf.Buf[0] = (UBYTE) (REPLY_CMD); VarsComm.BtModuleOutBuf.Buf[1] = (UBYTE) (VarsComm.ExtTx.Cmd); VarsComm.BtModuleOutBuf.Buf[2] = LOADER_ERR_BYTE(Status); VarsComm.BtModuleOutBuf.Buf[3] = LOADER_HANDLE(Status); VarsComm.BtModuleOutBuf.Buf[4] = (UBYTE)VarsComm.ExtTx.RemMsgSize; VarsComm.BtModuleOutBuf.Buf[5] = (UBYTE)(VarsComm.ExtTx.RemMsgSize >> 8); dBtSendMsg(VarsComm.BtModuleOutBuf.Buf, (UBYTE)(SIZE_OF_BTBUF - 2), (VarsComm.ExtTx.RemMsgSize + 6)); VarsComm.ExtTx.RemMsgSize -= (SIZE_OF_BTBUF - 8); VarsComm.UpdateState = 1; } break; case 1: { ULONG Length; if(dBtCheckForTxBuf()) { if (VarsComm.ExtTx.RemMsgSize > (SIZE_OF_BTBUF)) { /* Send max number of bytes */ VarsComm.ExtTx.RemMsgSize -= SIZE_OF_BTBUF; Length = SIZE_OF_BTBUF; } else { /* Buffer can hold the last part of the requested data */ Length = VarsComm.ExtTx.RemMsgSize; VarsComm.ExtTx.RemMsgSize = 0; *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } Handle =(UBYTE)(VarsComm.ExtTx.SrcHandle); pMapLoader->pFunc(READ, &Handle, (VarsComm.BtModuleOutBuf.Buf), &Length); dBtSend(VarsComm.BtModuleOutBuf.Buf, Length); } } break; } } break; case UPD_SEARCH: { switch (VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { cCommsCloseConn0(&(VarsComm.UpdateState)); } break; case 2: { /* Now ready for the actual search */ for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if ((IOMapComm.BtDeviceTable[Tmp].DeviceStatus) & BT_DEVICE_KNOWN) { (IOMapComm.BtDeviceTable[Tmp].DeviceStatus) = (BT_DEVICE_AWAY | BT_DEVICE_KNOWN); } else { IOMapComm.BtDeviceTable[Tmp].DeviceStatus = BT_DEVICE_EMPTY; } } dBtSendBtCmd((UBYTE)MSG_BEGIN_INQUIRY, (UBYTE)BT_DEFAULT_INQUIRY_MAX, BT_DEFAULT_INQUIRY_TIMEOUT_LO, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } break; case 3: { /* this is the stop search flag */ /* - meaning that the search should be stopped */ if (1 == VarsComm.BtCmdData.ParamOne) { dBtSendBtCmd((UBYTE)MSG_CANCEL_INQUIRY, 0, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState) = 7; } else { /* when inquiry is running there is 2 alloable return answers */ /* either inquiry result or inquiry stopped */ if (MSG_INQUIRY_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { dBtResetTimeOut(); /* reset the cmd timeout */ Tmp = cCommInsertDevice(&(IOMapComm.BtInBuf.Buf[2]), &(IOMapComm.BtInBuf.Buf[9]), &(IOMapComm.BtInBuf.Buf[25]), (UBYTE) BT_DEVICE_UNKNOWN, &Tmp2); if (SIZE_OF_BT_DEVICE_TABLE > Tmp) { /* Remember to check for already existing entry ....*/ if (DEVICE_VERIFIED != Tmp2) { (IOMapComm.BtDeviceTable[Tmp].DeviceStatus) &= ~BT_DEVICE_AWAY; IOMapComm.BtDeviceCnt++; } } else { /* We will send a stop inquiry cmd as the table is full! */ dBtSendBtCmd((UBYTE)MSG_CANCEL_INQUIRY, 0, 0, NULL, NULL, NULL, NULL); } } if (MSG_INQUIRY_STOPPED == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { VarsComm.BtDeviceIndex = 0; /* Start looking for found devices at index 0 */ VarsComm.LookUpCnt = 0; /* how many times should we try to ask for the name */ (VarsComm.UpdateState)++; } } IOMapComm.BtInBuf.Buf[BT_CMD_BYTE] = 0; } break; case 4: { /* this is the stop search flag */ /* - meaning that the search should be stopped */ if (1 == VarsComm.BtCmdData.ParamOne) { (VarsComm.UpdateState) = 8; } else { /* Needs to run through the hole list as found devices can be placed anywhere */ /* in the table */ for (Tmp = (VarsComm.BtDeviceIndex); Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if ((BT_DEVICE_UNKNOWN == IOMapComm.BtDeviceTable[Tmp].DeviceStatus) || (BT_DEVICE_KNOWN == IOMapComm.BtDeviceTable[Tmp].DeviceStatus)) { VarsComm.BtDeviceIndex = (Tmp + 1); (VarsComm.UpdateState)++; dBtSendBtCmd((UBYTE)MSG_LOOKUP_NAME, 0, 0, (IOMapComm.BtDeviceTable[Tmp].BdAddr), NULL, NULL, NULL); break; } } if (SIZE_OF_BT_DEVICE_TABLE == Tmp) { (VarsComm.LookUpCnt)++; if (((VarsComm.LookUpCnt) < LOOKUPNO) && ((IOMapComm.BtDeviceNameCnt) != (IOMapComm.BtDeviceCnt))) { VarsComm.BtDeviceIndex = 0; } else { // all done SETBtStateIdle; *(VarsComm.pRetVal) = SUCCESS; } } } } break; case 5: { if (MSG_LOOKUP_NAME_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { Tmp2 = FALSE; /* Tmp2 used to indicate name change */ (IOMapComm.BtDeviceNameCnt)++; /* Try look the most obvious place in the device table */ Tmp = VarsComm.BtDeviceIndex - 1; if (TRUE != cCommCheckBdaddr((IOMapComm.BtDeviceTable[Tmp].BdAddr), &(IOMapComm.BtInBuf.Buf[2]))) { /* there was no match - now look the complete table */ for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if (TRUE == cCommCheckBdaddr((IOMapComm.BtDeviceTable[Tmp].BdAddr), &(IOMapComm.BtInBuf.Buf[2]))) { break; } } } if (Tmp < SIZE_OF_BT_DEVICE_TABLE) { /* Valid index with matching device adress found */ if (0 == IOMapComm.BtInBuf.Buf[9]) { if (0 == IOMapComm.BtDeviceTable[Tmp].Name[0]) { /* No valid name recvd and no valid name in table -> insert "No Name" */ cCommInsertBtName(IOMapComm.BtDeviceTable[Tmp].Name, (UBYTE*)NoName); } } else { /* Valid Name - check it against the one allready stored in the device table */ /* if it differs then update */ if (0 != strcmp((char const*)IOMapComm.BtDeviceTable[Tmp].Name, (char const*)&(IOMapComm.BtInBuf.Buf[9]))) { cCommInsertBtName(IOMapComm.BtDeviceTable[Tmp].Name, &(IOMapComm.BtInBuf.Buf[9])); Tmp2 = TRUE; } } if ((BT_DEVICE_KNOWN == (IOMapComm.BtDeviceTable[Tmp].DeviceStatus)) && (TRUE == Tmp2)) { dBtSendBtCmd((UBYTE)MSG_ADD_DEVICE, 0, 0, (IOMapComm.BtDeviceTable[Tmp].BdAddr), (IOMapComm.BtDeviceTable[Tmp].Name), (IOMapComm.BtDeviceTable[Tmp].ClassOfDevice), NULL); (VarsComm.UpdateState)++; } else { (VarsComm.UpdateState)--; } } /* Update devicestatus (name found) so it doesn't look up the name anymore */ IOMapComm.BtDeviceTable[Tmp].DeviceStatus |= BT_DEVICE_NAME; } if (MSG_LOOKUP_NAME_FAILURE == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if ((LOOKUPNO - 1) == VarsComm.LookUpCnt) { /* This is the last time we ask this device -> we will not get a valid name */ /* Try look the most obvious place in the device table */ Tmp = VarsComm.BtDeviceIndex - 1; if (TRUE != cCommCheckBdaddr((IOMapComm.BtDeviceTable[Tmp].BdAddr), &(IOMapComm.BtInBuf.Buf[2]))) { for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if (TRUE == cCommCheckBdaddr((IOMapComm.BtDeviceTable[Tmp].BdAddr), &(IOMapComm.BtInBuf.Buf[2]))) { break; } } if ((Tmp < SIZE_OF_BT_DEVICE_TABLE) && (BT_DEVICE_UNKNOWN == (IOMapComm.BtDeviceTable[Tmp].DeviceStatus))) { cCommInsertBtName(IOMapComm.BtDeviceTable[Tmp].Name, (UBYTE*) NoName); } } (IOMapComm.BtDeviceNameCnt)++; } (VarsComm.UpdateState)--; } IOMapComm.BtInBuf.Buf[BT_CMD_BYTE] = 0; } break; case 6: { /* Waiting for reply on add device command - List result */ if (MSG_LIST_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if (LR_SUCCESS == IOMapComm.BtInBuf.Buf[2]) { /* Return and go through the list*/ (VarsComm.UpdateState) -= 2; } else { pMapUi->Error = (UBYTE)IOMapComm.BtInBuf.Buf[2]; pMapUi->BluetoothState |= BT_ERROR_ATTENTION; } } } break; case 7: { /* here because search has been stopped by user during inquiry */ if (MSG_INQUIRY_STOPPED == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* table should be cleared as no names hes been inquired */ for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if ((IOMapComm.BtDeviceTable[Tmp].DeviceStatus) & BT_DEVICE_KNOWN) { (IOMapComm.BtDeviceTable[Tmp].DeviceStatus) = BT_DEVICE_KNOWN; } else { (IOMapComm.BtDeviceTable[Tmp].DeviceStatus) = BT_DEVICE_EMPTY; } IOMapComm.BtDeviceCnt = 0; IOMapComm.BtDeviceNameCnt = 0; } SETBtStateIdle; *(VarsComm.pRetVal) = SUCCESS; } } break; case 8: { for (Tmp = (VarsComm.BtDeviceIndex); Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if (BT_DEVICE_UNKNOWN == IOMapComm.BtDeviceTable[Tmp].DeviceStatus) { IOMapComm.BtDeviceTable[Tmp].DeviceStatus = BT_DEVICE_EMPTY; } } SETBtStateIdle; *(VarsComm.pRetVal) = SUCCESS; } break; } } break; case UPD_CONNECTREQ: { switch (VarsComm.UpdateState) { case 0: { dBtSendBtCmd((UBYTE)MSG_ACCEPT_CONNECTION, 1, 0, NULL, NULL, NULL, NULL); cCommCopyBdaddr((IOMapComm.BtConnectTable[0].BdAddr), &(IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 1])); (VarsComm.UpdateState)++; } break; case 1: { if (MSG_CONNECT_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* Check for successfull connection */ if (1 == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 1]) { /* Save the handle number and look up the name of the master */ IOMapComm.BtConnectTable[0].HandleNr = IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 2]; pMapUi->BluetoothState |= BT_STATE_CONNECTED; dBtSendBtCmd((UBYTE)MSG_LOOKUP_NAME, 0, 0, (IOMapComm.BtConnectTable[0].BdAddr), NULL, NULL, NULL); (VarsComm.UpdateState)++; } else { /* Unsuccessful connection */ SETBtStateIdle; *(VarsComm.pRetVal) = BTCONNECTFAIL; } } } break; case 2: { /* a close connection can happen during connection sequence - if this */ /* occurs for connection 0 then abort the rest of the sequence - OxFF */ /* is unused handle */ if ((MSG_CLOSE_CONNECTION_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) && (0xFF == IOMapComm.BtConnectTable[0].HandleNr)) { SETBtStateIdle; *(VarsComm.pRetVal) = BTCONNECTFAIL; } else { if (MSG_LOOKUP_NAME_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { Tmp = cCommInsertDevice(&(IOMapComm.BtInBuf.Buf[2]), &(IOMapComm.BtInBuf.Buf[9]), &(IOMapComm.BtInBuf.Buf[25]), (UBYTE) BT_DEVICE_KNOWN, &Tmp2); if (SIZE_OF_BT_DEVICE_TABLE > Tmp) { /* entry has been added or is allready existing in the devicetable */ cCommInsertBtName(IOMapComm.BtConnectTable[0].Name, &(IOMapComm.BtInBuf.Buf[9])); cCommCopyBdaddr((IOMapComm.BtConnectTable[0].BdAddr), &(IOMapComm.BtInBuf.Buf[2])); memcpy(IOMapComm.BtConnectTable[0].ClassOfDevice, IOMapComm.BtDeviceTable[Tmp].ClassOfDevice, SIZE_OF_CLASS_OF_DEVICE); dBtSendBtCmd((UBYTE)MSG_ADD_DEVICE, 0, 0, (IOMapComm.BtDeviceTable[Tmp].BdAddr), (IOMapComm.BtDeviceTable[Tmp].Name), (IOMapComm.BtDeviceTable[Tmp].ClassOfDevice), NULL); (VarsComm.UpdateState)++; } else { /* no room in the devicetable -> reject the request. Param2 is index in connect table */ dBtSendBtCmd((UBYTE)MSG_CLOSE_CONNECTION, IOMapComm.BtConnectTable[0].HandleNr, 0, NULL, NULL, NULL, NULL); SETBtStateIdle; *(VarsComm.pRetVal) = BTCONNECTFAIL; } } if (MSG_LOOKUP_NAME_FAILURE == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* not able to get the name - disconnect*/ dBtSendBtCmd((UBYTE)MSG_CLOSE_CONNECTION, IOMapComm.BtConnectTable[0].HandleNr, 0, NULL, NULL, NULL, NULL); *(VarsComm.pRetVal) = BTCONNECTFAIL; SETBtStateIdle; } } } break; case 3: { if (MSG_LIST_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if (LR_SUCCESS == IOMapComm.BtInBuf.Buf[2]) { /* All success - open stream (Data mode) */ dBtSendBtCmd((UBYTE)MSG_OPEN_STREAM, IOMapComm.BtConnectTable[0].HandleNr, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } else { /* no room in the BC4 -> reject the request */ dBtSendBtCmd((UBYTE)MSG_CLOSE_CONNECTION, IOMapComm.BtConnectTable[0].HandleNr, 0, NULL, NULL, NULL, NULL); *(VarsComm.pRetVal) = BTCONNECTFAIL; SETBtStateIdle; } } } break; case 4: { if (VarsComm.BtBcPinLevel) { IOMapComm.BtConnectTable[0].StreamStatus = 1; *(VarsComm.pRetVal) = SUCCESS; SETBtDataState; SETBtStateIdle; } } break; } } break; case UPD_CONNECT: { switch (VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { cCommsCloseConn0(&(VarsComm.UpdateState)); } break; case 2: { dBtSendBtCmd((UBYTE)MSG_CONNECT, 0, 0, IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].BdAddr, NULL, NULL, NULL); (VarsComm.UpdateState)++; } break; case 3: { if (MSG_CONNECT_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if (IOMapComm.BtInBuf.Buf[2] == 1) { IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].HandleNr = IOMapComm.BtInBuf.Buf[3]; pMapUi->BluetoothState |= BT_STATE_CONNECTED; //Now we need to copy the data to the connectiontable memcpy((IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].BdAddr), (IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].BdAddr), SIZE_OF_BDADDR); cCommInsertBtName(IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].Name, IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].Name); memcpy((IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].ClassOfDevice), (IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].ClassOfDevice), SIZE_OF_CLASS_OF_DEVICE); IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].DeviceStatus = BT_DEVICE_KNOWN; if (VarsComm.BtCmdData.ParamTwo == 1) { IOMapComm.BrickData.BtStateStatus |= BT_CONNECTION_1_ENABLE; } else { if (VarsComm.BtCmdData.ParamTwo == 2) { IOMapComm.BrickData.BtStateStatus |= BT_CONNECTION_2_ENABLE; } else { if (VarsComm.BtCmdData.ParamTwo == 3) { IOMapComm.BrickData.BtStateStatus |= BT_CONNECTION_3_ENABLE; } } } dBtSendBtCmd((UBYTE)MSG_ADD_DEVICE, 0, 0, (IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].BdAddr), (IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].Name), (IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].ClassOfDevice), NULL); (VarsComm.UpdateState)+=3; /* skip the pin code part */ } else { /* Connect request denied */ *(VarsComm.pRetVal) = BTCONNECTFAIL; SETBtStateIdle; } } if (MSG_REQUEST_PIN_CODE == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { *(VarsComm.pRetVal) = REQPIN; VarsComm.pValidPinCode = NULL; (VarsComm.UpdateState)++; } } break; case 4: { if (NULL != VarsComm.pValidPinCode) { memcpy((IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].PinCode), VarsComm.pValidPinCode, SIZE_OF_BT_PINCODE); dBtSendBtCmd((UBYTE)MSG_PIN_CODE, 0, 0, IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].BdAddr, NULL, NULL, (VarsComm.pValidPinCode)); (VarsComm.UpdateState)++; } if (MSG_CONNECT_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* if no pin code has been accepted then timeout indicated */ /* by connect failiure - it can only be failiure here */ *(VarsComm.pRetVal) = BTCONNECTFAIL; SETBtStateIdle; } } break; case 5: { if (MSG_CONNECT_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* Connect failiure can happen at any time */ *(VarsComm.pRetVal) = BTCONNECTFAIL; SETBtStateIdle; } if (MSG_PIN_CODE_ACK == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* return back and wait for connect ack */ (VarsComm.UpdateState) = 3; } } break; case 6: { if (MSG_LIST_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } } break; } } break; case UPD_DISCONNECT: { switch (VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { if (BLUETOOTH_HANDLE_UNDEFIEND != IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamOne].HandleNr) { VarsComm.BtCmdData.ParamOne = IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamOne].HandleNr; dBtSendBtCmd((UBYTE)MSG_CLOSE_CONNECTION, VarsComm.BtCmdData.ParamOne, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } else { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } } break; case 2: { /* look for right message and right handle */ if ((MSG_CLOSE_CONNECTION_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) && (VarsComm.BtCmdData.ParamOne == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE + 2])) { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } } break; } } break; case UPD_DISCONNECTALL: { switch (VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { cCommsDisconnectAll(&(VarsComm.UpdateState)); } break; case 2: { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } break; } } break; case UPD_REMOVEDEVICE: { switch (VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { cCommsCloseConn0(&(VarsComm.UpdateState)); } break; case 2: { dBtSendBtCmd((UBYTE)MSG_REMOVE_DEVICE, 0, 0, IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].BdAddr, NULL, NULL, NULL); IOMapComm.BtDeviceTable[VarsComm.BtCmdData.ParamOne].DeviceStatus = BT_DEVICE_EMPTY; (VarsComm.UpdateState)++; } break; case 3: { if (MSG_LIST_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } } break; } } break; case UPD_PINREQ: { /* This is pincode request from the outside - always conn 0*/ switch (VarsComm.UpdateState) { case 0: { if (NULL != VarsComm.pValidPinCode) { memcpy((IOMapComm.BtConnectTable[0].PinCode), VarsComm.pValidPinCode, SIZE_OF_BT_PINCODE); dBtSendBtCmd((UBYTE)MSG_PIN_CODE, 0, 0, (IOMapComm.BtConnectTable[0].BdAddr), NULL, NULL, (VarsComm.pValidPinCode)); (VarsComm.UpdateState)++; } } break; case 1: { if (MSG_PIN_CODE_ACK == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { SETBtStateIdle; } } break; } } break; case UPD_VISIBILITY: { switch (VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { cCommsCloseConn0(&(VarsComm.UpdateState)); } break; case 2: { dBtSendBtCmd((UBYTE)MSG_SET_DISCOVERABLE, VarsComm.BtCmdData.ParamOne, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } break; case 3: { if (MSG_DISCOVERABLE_ACK == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if (VarsComm.BtCmdData.ParamOne == 1) { IOMapComm.BrickData.BtStateStatus |= BT_BRICK_VISIBILITY; pMapUi->BluetoothState |= BT_STATE_VISIBLE; } else { IOMapComm.BrickData.BtStateStatus &= ~BT_BRICK_VISIBILITY; pMapUi->BluetoothState &= ~BT_STATE_VISIBLE; } *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } } break; } } break; case UPD_OFF: { switch (VarsComm.UpdateState) { case 0: { cCommsSetCmdMode(&(VarsComm.UpdateState)); } break; case 1: { cCommsDisconnectAll(&(VarsComm.UpdateState)); } break; case 2: { dBtSendBtCmd((UBYTE)MSG_SET_BRICK_STATUSBYTE, BT_DISABLE, 0, NULL, NULL, NULL, NULL); (VarsComm.UpdateState)++; } break; case 3: { if (MSG_SET_BRICK_STATUSBYTE_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { if (IOMapComm.BtInBuf.Buf[2] == LR_SUCCESS) { SETBtOff; pMapUi->BluetoothState = BT_STATE_OFF; pMapUi->Flags |= UI_REDRAW_STATUS; } *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } } break; } } break; case UPD_SENDDATA: { switch (VarsComm.UpdateState) { case 0: { if (1 == IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].StreamStatus) { /* Stream is allready open for the requested channel */ (VarsComm.UpdateState) += 2; } else { /* Stream not open - try open it*/ (VarsComm.UpdateState)++; } } break; case 1: { cCommsOpenStream(&(VarsComm.UpdateState)); } break; case 2: { /* Stream is now opened now send the data */ IOMapComm.BtInBuf.Buf[0] = 0; dBtSendMsg((VarsComm.BtModuleOutBuf.Buf), VarsComm.BtCmdData.ParamOne, (UWORD)(VarsComm.BtCmdData.ParamOne)); (VarsComm.UpdateState)++; } break; case 3: { if(dBtCheckForTxBuf()) { if (VarsComm.BtCmdData.ParamThree) { VarsComm.ExtTx.Timer = 0; (VarsComm.UpdateState)++; } else { *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } } } break; case 4: { if (0x02 == IOMapComm.BtInBuf.Buf[0]) { /* a reply has been received now release the send sequence */ *(VarsComm.pRetVal) = SUCCESS; SETBtStateIdle; } else { if (++VarsComm.ExtTx.Timer > BTSTREAMTOUT) { *(VarsComm.pRetVal) = BTTIMEOUT; SETBtStateIdle; } } } break; } } break; default: { /* This is idle */ VarsComm.UpdateState = 0; } break; } } UWORD cCommCopyBdaddr(UBYTE *pDst, UBYTE *pSrc) { memcpy(pDst, pSrc, SIZE_OF_BDADDR); return((UWORD) SIZE_OF_BDADDR); } UWORD cCommCheckBdaddr(UBYTE *pAdr, UBYTE *pSrc) { UWORD RetVal; RetVal = FALSE; if (0 == memcmp((UBYTE*)pAdr, pSrc, SIZE_OF_BDADDR)) { RetVal = TRUE; } return(RetVal); } UWORD cCommInsertBtName(UBYTE *pDst, UBYTE *pSrc) { UBYTE Cnt; Cnt = 0; /* Complete brick name */ while ((pSrc[Cnt]) && (Cnt < (SIZE_OF_BT_NAME - 1))) { pDst[Cnt] = pSrc[Cnt]; Cnt++; } /* Fill remaining up with zeros */ while (Cnt < SIZE_OF_BT_NAME) { pDst[Cnt] = 0; Cnt++; } return((UWORD)SIZE_OF_BT_NAME); } UWORD cCommInsertDevice(UBYTE *pBdaddr, UBYTE *pName, UBYTE *pCod, UBYTE DeviceStatus, UBYTE *pAddInfo) { UWORD Tmp; UWORD RtnVal; RtnVal = FALSE; *pAddInfo = DEVICE_VERIFIED; for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if ((TRUE == cCommCheckBdaddr((IOMapComm.BtDeviceTable[Tmp].BdAddr), pBdaddr)) && (IOMapComm.BtDeviceTable[Tmp].DeviceStatus != BT_DEVICE_EMPTY)) { if ((IOMapComm.BtDeviceTable[Tmp].DeviceStatus) & BT_DEVICE_AWAY) { *pAddInfo = DEVICE_UPDATED; (IOMapComm.BtDeviceTable[Tmp].DeviceStatus) &= ~BT_DEVICE_AWAY; } if (BT_DEVICE_UNKNOWN == IOMapComm.BtDeviceTable[Tmp].DeviceStatus) { /* Former unknown adresses can be upgraded - downgrading is not possible */ IOMapComm.BtDeviceTable[Tmp].DeviceStatus = DeviceStatus; } if (pCod != NULL) { /* Class of device can also upgraded - never downgraded to 0 */ memcpy(&(IOMapComm.BtDeviceTable[Tmp].ClassOfDevice), pCod, SIZE_OF_CLASS_OF_DEVICE); } if ((*pName) != 0) { /* Only upgrade name if name is received */ cCommInsertBtName(IOMapComm.BtDeviceTable[Tmp].Name, pName); } RtnVal = TRUE; /* Break out - entry can only be found once */ break; } } if (FALSE == RtnVal) { for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if (IOMapComm.BtDeviceTable[Tmp].DeviceStatus == BT_DEVICE_EMPTY) { *pAddInfo = DEVICE_INSERTED; IOMapComm.BtDeviceTable[Tmp].DeviceStatus = DeviceStatus; cCommCopyBdaddr((IOMapComm.BtDeviceTable[Tmp].BdAddr), pBdaddr); cCommInsertBtName(IOMapComm.BtDeviceTable[Tmp].Name, pName); if (NULL != pCod) { memcpy((IOMapComm.BtDeviceTable[Tmp].ClassOfDevice), pCod, SIZE_OF_CLASS_OF_DEVICE); } else { memset((IOMapComm.BtDeviceTable[Tmp].ClassOfDevice), 0, SIZE_OF_CLASS_OF_DEVICE); } RtnVal = TRUE; break; } } } /* Function returns SIZE_OF_BT_DEVICE_TABLE if device is not in the list */ return(Tmp); } void cCommsSetCmdMode(UBYTE *pNextState) { switch(VarsComm.CmdSwitchCnt) { case 0: { if (BT_ARM_CMD_MODE != VarsComm.BtState) { cCommClearStreamStatus(); VarsComm.BtCmdModeWaitCnt = 0; VarsComm.CmdSwitchCnt++; } else { /* allready in CMD mode - Exit */ VarsComm.CmdSwitchCnt = 0; (*pNextState)++; } } break; case 1: { /* stream status has been cleared now wait until buffers has been emptied */ if (TRUE == dBtTxEnd()) { /* Wait 100 ms after last byte has been sent to BC4 - else BC4 can crash */ if (++(VarsComm.BtCmdModeWaitCnt) > 100) { dBtClearArm7CmdSignal(); VarsComm.CmdSwitchCnt++; } } } break; case 2: { if (VarsComm.BtBcPinLevel == 0) { /* Bluecore has entered cmd mode */ SETBtCmdState; VarsComm.CmdSwitchCnt = 0; (*pNextState)++; } } break; default: { VarsComm.CmdSwitchCnt = 0; } break; } } void cCommsOpenStream(UBYTE *pNextState) { switch(VarsComm.StreamStateCnt) { case 0: { if (SIZE_OF_BT_CONNECT_TABLE > VarsComm.BtCmdData.ParamTwo) { /* first check if there is a connection on the requested channel */ if ('\0' != IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].Name[0]) { if (1 == IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].StreamStatus) { /* Stream is allready open - continue */ (*pNextState)++; } else { /* There is a connection on requested channel proceed */ VarsComm.StreamStateCnt = 1; } } else { /* Error - no connecteion on requested channel - exit */ *(VarsComm.pRetVal) = (UWORD)ERR_COMM_CHAN_NOT_READY; *(VarsComm.pRetVal) |= 0x8000; SETBtStateIdle; } } else { /* Error - Illegal channel no - exit */ *(VarsComm.pRetVal) = (UWORD)ERR_COMM_CHAN_INVALID; *(VarsComm.pRetVal) |= 0x8000; SETBtStateIdle; } } break; case 1: { cCommsSetCmdMode(&(VarsComm.StreamStateCnt)); } break; case 2: { cCommsCloseConn0(&(VarsComm.StreamStateCnt)); } break; case 3: { /* Open stream on the specified channel */ VarsComm.StreamStateCnt = 4; dBtSendBtCmd((UBYTE)MSG_OPEN_STREAM, IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].HandleNr, 0, NULL, NULL, NULL, NULL); } break; case 4: { if (VarsComm.BtBcPinLevel) { SETBtDataState; IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamTwo].StreamStatus = 1; VarsComm.StreamStateCnt = 0; (*pNextState)++; } } break; default: { VarsComm.StreamStateCnt = 0; } break; } } void cCommsCloseConn0(UBYTE *pNextState) { switch(VarsComm.CloseConn0Cnt) { case 0: { if ('\0' != IOMapComm.BtConnectTable[0].Name[0]) { /* now disconnect channel 0 */ VarsComm.CloseConn0Cnt = 1; dBtSendBtCmd((UBYTE)MSG_CLOSE_CONNECTION, IOMapComm.BtConnectTable[0].HandleNr, 0, NULL, NULL, NULL, NULL); } else { (*pNextState)++; } } break; case 1: { if (MSG_CLOSE_CONNECTION_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { VarsComm.CloseConn0Cnt = 0; (*pNextState)++; } } break; default: { VarsComm.CloseConn0Cnt = 0; } break; } } void cCommsDisconnectAll(UBYTE *pNextState) { switch(VarsComm.DiscAllCnt) { case 0: { VarsComm.BtCmdData.ParamOne = 0; (VarsComm.DiscAllCnt)++; } break; case 1: { while (('\0' == IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamOne].Name[0]) && (VarsComm.BtCmdData.ParamOne < 4)) { VarsComm.BtCmdData.ParamOne++; } if (VarsComm.BtCmdData.ParamOne < 4) { /* now disconnect selected channel */ dBtSendBtCmd((UBYTE)MSG_CLOSE_CONNECTION, IOMapComm.BtConnectTable[VarsComm.BtCmdData.ParamOne].HandleNr, 0, NULL, NULL, NULL, NULL); VarsComm.BtCmdData.ParamOne++; (VarsComm.DiscAllCnt)++; } else { /* no more connections - move on */ (VarsComm.DiscAllCnt) = 0; (*pNextState)++; } } break; case 2: { if (MSG_CLOSE_CONNECTION_RESULT == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE]) { /* Go back and check for more connections to close */ (VarsComm.DiscAllCnt)--; } } break; } } void cCommsBtReset(UBYTE *pNextState) { switch(VarsComm.ResetBtCnt) { case 0: { /* Setup Reset sequence */ VarsComm.BtResetTimeCnt = 0; VarsComm.ResetBtCnt = 1; dBtSetBcResetPinLow(); } break; case 1: { /* Reset should be held low for a certain time "BLUECORE_RESET_TIME" */ VarsComm.BtResetTimeCnt++; if (VarsComm.BtResetTimeCnt > BLUECORE_RESET_TIME) { dBtSetBcResetPinHigh(); VarsComm.BtWaitTimeCnt = 0; VarsComm.ResetBtCnt = 2; } } break; case 2: { /* Wait after reset is released either wait a minimum time or wait for the reset indication telegram */ VarsComm.BtWaitTimeCnt++; if ((VarsComm.BtWaitTimeCnt > BLUECORE_WAIT_BEFORE_INIT) || (MSG_RESET_INDICATION == IOMapComm.BtInBuf.Buf[BT_CMD_BYTE])) { memset(&(IOMapComm.BtDeviceTable), 0, sizeof(IOMapComm.BtDeviceTable)); cCommClrConnTable(); VarsComm.ResetBtCnt = 3; } } break; case 3: { SETBtCmdState; VarsComm.ResetBtCnt = 0; (*pNextState)++; } break; } } UWORD cCommReq(UBYTE Cmd, UBYTE Param1, UBYTE Param2, UBYTE Param3, UBYTE *pName, UWORD *pRetVal) { ULONG Length; UWORD ReturnVal; SBYTE foundIndex= 0; ReturnVal = BTBUSY; *pRetVal = BTBUSY; if ((UPD_IDLE == (VarsComm.ActiveUpdate)) || ((UPD_SEARCH == (VarsComm.ActiveUpdate)) && (STOPSEARCH == Cmd))) { ReturnVal = SUCCESS; *pRetVal = INPROGRESS; VarsComm.pRetVal = pRetVal; switch(Cmd) { case SENDFILE: { ReturnVal = SUCCESS; /* No file is currently beeing send - Now open the file */ VarsComm.ExtTx.SrcHandle = pMapLoader->pFunc(OPENREAD, pName, NULL, &Length); VarsComm.ExtTx.RemFileSize = Length; VarsComm.ExtTx.SlotNo = Param1; VarsComm.BtCmdData.ParamTwo = Param1; /* This is used to open the correct stream */ if (0x8000 > VarsComm.ExtTx.SrcHandle) { /* Source file is ok - go ahead */ VarsComm.ActiveUpdate = UPD_SENDFILE; VarsComm.ExtTx.Timer = 0; VarsComm.ExtTx.Cmd = WRITE; cCommCopyFileName(VarsComm.ExtTx.FileName, pName); } else { /* Error in opening source file for read - file do not exist */ ReturnVal = FILETX_SRCMISSING; } } break; case CONNECTBYNAME: // redo Param1, then fall through existing CONNECT code foundIndex= cCommSearchBTDevTableForName(pName); if(foundIndex != -1) Param1= foundIndex; /* fall through */ case CONNECT: { if (BLUETOOTH_HANDLE_UNDEFIEND == IOMapComm.BtConnectTable[Param2].HandleNr && foundIndex != -1) { /* Connection not occupied */ VarsComm.ActiveUpdate = UPD_CONNECT; VarsComm.BtCmdData.ParamOne = Param1; VarsComm.BtCmdData.ParamTwo = Param2; } else { /* Connection occupied */ ReturnVal = BTCONNECTFAIL; *pRetVal = BTCONNECTFAIL; } } break; case DISCONNECT: { VarsComm.ActiveUpdate = UPD_DISCONNECT; VarsComm.BtCmdData.ParamOne = Param1; } break; case DISCONNECTALL: { VarsComm.ActiveUpdate = UPD_DISCONNECTALL; } break; case SEARCH: { VarsComm.ActiveUpdate = UPD_SEARCH; IOMapComm.BtDeviceNameCnt = 0; IOMapComm.BtDeviceCnt = 0; VarsComm.BtCmdData.ParamOne = 0; } break; case STOPSEARCH: { if (UPD_SEARCH == (VarsComm.ActiveUpdate)) { VarsComm.BtCmdData.ParamOne = 1; } else { *pRetVal = SUCCESS; } } break; case REMOVEDEVICE: { VarsComm.ActiveUpdate = UPD_REMOVEDEVICE; VarsComm.BtCmdData.ParamOne = Param1; } break; case VISIBILITY: { VarsComm.ActiveUpdate = UPD_VISIBILITY; VarsComm.BtCmdData.ParamOne = Param1; } break; case SETCMDMODE: { VarsComm.ActiveUpdate = UPD_REQCMDMODE; } break; case FACTORYRESET: { VarsComm.ActiveUpdate = UPD_FACTORYRESET; } break; case BTON: { if (BT_STATE_OFF & (pMapUi->BluetoothState)) { VarsComm.ActiveUpdate = UPD_RESET; } else { /* Device is already on*/ *pRetVal = SUCCESS; } } break; case BTOFF: { VarsComm.ActiveUpdate = UPD_OFF; } break; case SENDDATA: { /* Param2 indicates the port that the data should be */ /* be sent on - param1 indicates the number of data */ /* to be sent. pName is the pointer to the data */ if (Param1 <= sizeof(VarsComm.BtModuleOutBuf.Buf)) { if ('\0' != IOMapComm.BtConnectTable[Param2].Name[0]) { VarsComm.BtCmdData.ParamOne = Param1; VarsComm.BtCmdData.ParamTwo = Param2; VarsComm.BtCmdData.ParamThree = Param3; memcpy((VarsComm.BtModuleOutBuf.Buf), pName, Param1); VarsComm.ActiveUpdate = UPD_SENDDATA; } else { ReturnVal = (UWORD)ERR_COMM_CHAN_NOT_READY; ReturnVal |= 0x8000; } } else { ReturnVal = (UWORD)ERR_COMM_BUFFER_FULL; ReturnVal |= 0x8000; } } break; case OPENSTREAM: { VarsComm.BtCmdData.ParamTwo = Param2; VarsComm.ActiveUpdate = UPD_OPENSTREAM; } break; case SETBTNAME: { VarsComm.ActiveUpdate = UPD_BRICKNAME; } break; case EXTREAD: { VarsComm.ActiveUpdate = UPD_EXTREAD; } break; case PINREQ: { /* This is an incomming pinrequest for connection on connection 0 because */ /* ActiveUpdate is idle (if it was incomming it is not idle) */ cCommCopyBdaddr((IOMapComm.BtConnectTable[0].BdAddr), &(IOMapComm.BtInBuf.Buf[2])); pMapUi->BluetoothState |= (BT_CONNECT_REQUEST | BT_PIN_REQUEST); VarsComm.pValidPinCode = NULL; VarsComm.ActiveUpdate = UPD_PINREQ; } break; case CONNECTREQ: { VarsComm.ActiveUpdate = UPD_CONNECTREQ; } break; } } return(ReturnVal); } void cCommPinCode(UBYTE *pPinCode) { VarsComm.pValidPinCode = pPinCode; if (REQPIN == (*(VarsComm.pRetVal))) { *(VarsComm.pRetVal) = INPROGRESS; } } void cCommClrConnTable(void) { UBYTE Tmp; for (Tmp = 0; Tmp < SIZE_OF_BT_CONNECT_TABLE; Tmp++) { CLEARConnEntry(Tmp); } IOMapComm.BrickData.BtStateStatus &= ~(BT_CONNECTION_0_ENABLE | BT_CONNECTION_1_ENABLE | BT_CONNECTION_2_ENABLE | BT_CONNECTION_3_ENABLE); pMapUi->BluetoothState &= ~BT_STATE_CONNECTED; pMapUi->Flags |= UI_REDRAW_STATUS; } /* search the BT table */ SBYTE cCommSearchBTDevTableForName(UBYTE *name) { UBYTE Tmp; for (Tmp = 0; Tmp < SIZE_OF_BT_DEVICE_TABLE; Tmp++) { if (0 == strcmp((char*)(IOMapComm.BtDeviceTable[Tmp].Name), (char*)name)) return Tmp; } return -1; } nxt-firmware-1.29.7/src/c_comm.h000066400000000000000000000054451466344546000164560ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_comm.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_comm $ // // Platform C // #ifndef C_COMM #define C_COMM #define BLUECORE_RESET_TIME 100 // Time in mS #define BLUECORE_WAIT_BEFORE_INIT 5000 // Time in mS #define BLUETOOTH_HANDLE_UNDEFIEND 0xFF /* Constants related to BtAdrStatus*/ enum { COLDBOOT, INITIALIZED, BTADRERROR }; enum { USB_CH, BT_CH, HISPEED_CH, NO_OF_CHANNELS }; /* enum reffering to BT update */ enum { UPD_BRICKNAME, UPD_FACTORYRESET, UPD_OPENSTREAM, UPD_REQCMDMODE, UPD_CONNECT, UPD_CONNECTREQ, UPD_PINREQ, UPD_DISCONNECT, UPD_DISCONNECTALL, UPD_REMOVEDEVICE, UPD_SEARCH, UPD_RESET, UPD_EXTREAD, UPD_SENDFILE, UPD_OFF, UPD_VISIBILITY, UPD_SENDDATA, UPD_IDLE }; /* Constants reffering to Protocol */ enum { DIRECT_CMD = 0x00, SYSTEM_CMD = 0x01, REPLY_CMD = 0x02, #ifdef ARMDEBUG DEBUG_CMD = 0x0d, #endif NO_REPLY_BIT = 0x80 }; typedef struct { ULONG RemFileSize; UWORD RemMsgSize; UWORD SrcHandle; UWORD DstHandle; UWORD Timer; UBYTE FileName[FILENAME_LENGTH + 1]; UBYTE Cmd; UBYTE SlotNo; }EXTTX; typedef struct { UBYTE Buf[256]; UWORD InPtr; UWORD OutPtr; }BTDATA; typedef struct { UBYTE Buf[256]; UWORD InPtr; UWORD OutPtr; }HSDATA; typedef struct { UBYTE Status; UBYTE Type; UBYTE Handle; UBYTE Cmd; }EXTMODE; typedef struct { UBYTE ParamOne; UBYTE ParamTwo; UBYTE ParamThree; }BTCMD; typedef struct { UBYTE BtUpdateDataConnectNr; UBYTE BtBcPinLevel; UBYTE BtResetTimeCnt; UWORD BtWaitTimeCnt; BTDATA BtModuleInBuf; BTDATA BtModuleOutBuf; BTCMD BtCmdData; UBYTE HsState; HSDATA HsModuleInBuf; HSDATA HsModuleOutBuf; EXTTX ExtTx; EXTMODE ExtMode[NO_OF_CHANNELS]; UBYTE ActiveUpdate; UBYTE UpdateState; UBYTE BtDeviceIndex; UBYTE CmdSwitchCnt; UBYTE StreamStateCnt; UBYTE CloseConn0Cnt; UBYTE DiscAllCnt; UBYTE ResetBtCnt; UBYTE BtState; UWORD *pRetVal; UWORD RetVal; UBYTE *pValidPinCode; UBYTE LookUpCnt; UBYTE BtAdrStatus; UBYTE BtCmdModeWaitCnt; }VARSCOMM; void cCommInit(void* pHeader); void cCommCtrl(void); void cCommExit(void); extern const HEADER cComm; #endif nxt-firmware-1.29.7/src/c_comm.iom000066400000000000000000000113341466344546000170050ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 9-06-08 14:57 $ // // Filename $Workfile:: c_comm.iom $ // // Version $Revision:: 2 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_comm $ // // Platform C // #ifndef CCOMM_IOM #define CCOMM_IOM #define pMapComm ((IOMAPCOMM*)(pHeaders[ENTRY_COMM]->pIOMap)) #define SIZE_OF_USBBUF 64 #define USB_PROTOCOL_OVERHEAD 1 + 1 /* Command type byte + Command */ #define SIZE_OF_USBDATA (SIZE_OF_USBBUF - USB_PROTOCOL_OVERHEAD) #define SIZE_OF_HSBUF 128 #define SIZE_OF_BTBUF 128 #define BT_CMD_BYTE 1 #define SIZE_OF_BT_DEVICE_TABLE 30 #define SIZE_OF_BT_CONNECT_TABLE 4 /* Index 0 is alway incomming connections */ #define MAX_BT_MSG_SIZE 60000L #define BT_DEFAULT_INQUIRY_MAX 0 /* Unlimited no */ #define BT_DEFAULT_INQUIRY_TIMEOUT_LO 15 /* 15 x 1,28 Sec = 19,2 Sec */ // Constants reffering to BtState enum { BT_ARM_OFF, BT_ARM_CMD_MODE, BT_ARM_DATA_MODE, }; //Constant reffering to BtStateStatus #define BT_BRICK_VISIBILITY 0x01 #define BT_BRICK_PORT_OPEN 0x02 #define BT_CONNECTION_0_ENABLE 0x10 #define BT_CONNECTION_1_ENABLE 0x20 #define BT_CONNECTION_2_ENABLE 0x40 #define BT_CONNECTION_3_ENABLE 0x80 //Constant reffering to BtHwStatus #define BT_ENABLE 0x00 #define BT_DISABLE 0x01 // Constants reffering to HsFlags enum { HS_UPDATE = 1 }; // Constants reffering to HsState enum { HS_INITIALISE = 1, HS_INIT_RECEIVER, HS_SEND_DATA, HS_DISABLE }; //Constants refering to DeviceStatus within DeviceTable enum { BT_DEVICE_EMPTY = 0x00, BT_DEVICE_UNKNOWN = 0x01, BT_DEVICE_KNOWN = 0x02, BT_DEVICE_NAME = 0x40, BT_DEVICE_AWAY = 0x80 }; /* Interface between command other modules */ enum { SENDFILE, SEARCH, STOPSEARCH, CONNECT, DISCONNECT, DISCONNECTALL, REMOVEDEVICE, VISIBILITY, SETCMDMODE, OPENSTREAM, SENDDATA, FACTORYRESET, BTON, BTOFF, SETBTNAME, EXTREAD, PINREQ, CONNECTREQ, CONNECTBYNAME }; enum { LR_SUCCESS = 0x50, LR_COULD_NOT_SAVE, LR_STORE_IS_FULL, LR_ENTRY_REMOVED, LR_UNKOWN_ADDR }; enum { USB_CMD_READY = 0x01, BT_CMD_READY = 0x02, HS_CMD_READY = 0x04 }; typedef struct { UBYTE Buf[SIZE_OF_USBBUF]; UBYTE InPtr; UBYTE OutPtr; UBYTE Spare1; UBYTE Spare2; }USBBUF; typedef struct { UBYTE Buf[SIZE_OF_HSBUF]; UBYTE InPtr; UBYTE OutPtr; UBYTE Spare1; UBYTE Spare2; }HSBUF; typedef struct { UBYTE Buf[SIZE_OF_BTBUF]; UBYTE InPtr; UBYTE OutPtr; UBYTE Spare1; UBYTE Spare2; }BTBUF; typedef struct { UBYTE Name[SIZE_OF_BT_NAME]; UBYTE ClassOfDevice[SIZE_OF_CLASS_OF_DEVICE]; UBYTE BdAddr[SIZE_OF_BDADDR]; UBYTE DeviceStatus; UBYTE Spare1; UBYTE Spare2; UBYTE Spare3; }BDDEVICETABLE; typedef struct { UBYTE Name[SIZE_OF_BT_NAME]; UBYTE ClassOfDevice[SIZE_OF_CLASS_OF_DEVICE]; UBYTE PinCode[16]; UBYTE BdAddr[SIZE_OF_BDADDR]; UBYTE HandleNr; UBYTE StreamStatus; UBYTE LinkQuality; UBYTE Spare; }BDCONNECTTABLE; typedef struct { UBYTE Name[SIZE_OF_BT_NAME]; UBYTE BluecoreVersion[2]; UBYTE BdAddr[SIZE_OF_BDADDR]; UBYTE BtStateStatus; UBYTE BtHwStatus; UBYTE TimeOutValue; UBYTE Spare1; UBYTE Spare2; UBYTE Spare3; }BRICKDATA; typedef struct { UWORD (*pFunc)(UBYTE, UBYTE, UBYTE, UBYTE, UBYTE*, UWORD*); void (*pFunc2)(UBYTE*); // BT related entries BDDEVICETABLE BtDeviceTable[SIZE_OF_BT_DEVICE_TABLE]; BDCONNECTTABLE BtConnectTable[SIZE_OF_BT_CONNECT_TABLE]; //General brick data BRICKDATA BrickData; BTBUF BtInBuf; BTBUF BtOutBuf; // HI Speed related entries HSBUF HsInBuf; HSBUF HsOutBuf; // USB related entries USBBUF UsbInBuf; USBBUF UsbOutBuf; USBBUF UsbPollBuf; UBYTE BtDeviceCnt; UBYTE BtDeviceNameCnt; UBYTE HsFlags; UBYTE HsSpeed; UBYTE HsState; UBYTE UsbState; }IOMAPCOMM; #endif nxt-firmware-1.29.7/src/c_display.c000066400000000000000000000512211466344546000171540ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author: Dkflebun $ // // Revision date $Date: 9-06-08 13:35 $ // // Filename $Workfile:: c_display.c $ // // Version $Revision: 2 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_disp $ // // Platform C // #include #include "stdconst.h" #include "modules.h" #include "c_display.iom" #include "c_display.h" #include "d_display.h" static IOMAPDISPLAY IOMapDisplay; static VARSDISPLAY VarsDisplay; const HEADER cDisplay = { 0x000A0001L, "Display", cDisplayInit, cDisplayCtrl, cDisplayExit, (void *)&IOMapDisplay, (void *)&VarsDisplay, (UWORD)sizeof(IOMapDisplay), (UWORD)sizeof(VarsDisplay), 0x0000 //Code size - not used so far }; const SCREEN_CORDINATE SCREEN_CORDINATES[SCREENS] = { { 0, 0,DISPLAY_WIDTH,DISPLAY_HEIGHT }, // Background { 0, 8,DISPLAY_WIDTH,DISPLAY_HEIGHT - 8 }, // Large { 0, 8,DISPLAY_WIDTH,24 } // Small }; const SCREEN_CORDINATE SELECT_FRAME_CORDINATES = { 38,41,24,24 }; const SCREEN_CORDINATE MENUICON_CORDINATES[MENUICONS] = { { DISPLAY_MENUICONS_X_OFFS,DISPLAY_MENUICONS_Y,24,24 }, // Left { DISPLAY_MENUICONS_X_OFFS + DISPLAY_MENUICONS_X_DIFF,DISPLAY_MENUICONS_Y,24,24 }, // Center { DISPLAY_MENUICONS_X_OFFS + DISPLAY_MENUICONS_X_DIFF * 2,DISPLAY_MENUICONS_Y,24,24 },// Right }; const SCREEN_CORDINATE STATUSICON_CORDINATES[STATUSICONS] = { { 0, 0,12, 8 }, // Bluetooth { 12, 0,12, 8 }, // Usb { 76, 0,12, 8 }, // Vm { 88, 0,12, 8 } // Battery }; const SCREEN_CORDINATE STEPICON_CORDINATES[STEPICONS] = { { 11,16,11,16 }, // Step 1 { 28,16,11,16 }, // Step 2 { 45,16,11,16 }, // Step 3 { 62,16,11,16 }, // Step 4 { 79,16,11,16 } // Step 5 }; void cDisplaySetPixel(UBYTE X,UBYTE Y) { if ((X < DISPLAY_WIDTH) && (Y < DISPLAY_HEIGHT)) { IOMapDisplay.Display[(Y / 8) * DISPLAY_WIDTH + X] |= (1 << (Y % 8)); } } void cDisplayClrPixel(UBYTE X,UBYTE Y) { if ((X < DISPLAY_WIDTH) && (Y < DISPLAY_HEIGHT)) { IOMapDisplay.Display[(Y / 8) * DISPLAY_WIDTH + X] &= ~(1 << (Y % 8)); } } void cDisplayChar(FONT *pFont,UBYTE On,UBYTE X,UBYTE Y,UBYTE Char) { UBYTE *pSource; UBYTE FontWidth; UBYTE FontHeight; UBYTE Items; UBYTE Item; UBYTE TmpY; Items = pFont->ItemsX * pFont->ItemsY; Item = Char - ' '; if (Item < Items) { FontWidth = pFont->ItemPixelsX; pSource = (UBYTE*)&pFont->Data[Item * FontWidth]; while (FontWidth--) { TmpY = 0; FontHeight = pFont->ItemPixelsY; while (FontHeight--) { if (On == TRUE) { if (((*pSource) & (1 << TmpY))) { cDisplaySetPixel(X,Y + TmpY); } else { cDisplayClrPixel(X,Y + TmpY); } } else { if (((*pSource) & (1 << TmpY))) { cDisplayClrPixel(X,Y + TmpY); } else { cDisplaySetPixel(X,Y + TmpY); } } TmpY++; } X++; pSource++; } } } void cDisplayString(FONT *pFont,UBYTE X,UBYTE Y,UBYTE *pString) { UBYTE *pSource; UBYTE *pDestination; UBYTE FontWidth; UBYTE Line; UBYTE Items; UBYTE Item; Line = (Y & 0xF8) / 8; Items = pFont->ItemsX * pFont->ItemsY; pDestination = (UBYTE*)&IOMapDisplay.Display[Line * DISPLAY_WIDTH + X]; while (*pString) { Item = *pString - ' '; if (Item < Items) { FontWidth = pFont->ItemPixelsX; pSource = (UBYTE*)&pFont->Data[Item * FontWidth]; while (FontWidth--) { *pDestination = *pSource; pDestination++; pSource++; } } pString++; } } void cDisplayUpdateScreen(SCREEN_CORDINATE *pCord,BMPMAP *pBitmap) { UBYTE *pSource; UBYTE *pDestination; UBYTE Line; UBYTE Lines; if (pBitmap) { if ((((pBitmap->StartY + pCord->StartY) & 0x07) == 0) && ((pBitmap->PixelsY & 0x07) == 0)) { pSource = pBitmap->Data; Line = (pBitmap->StartY + pCord->StartY) / 8; Lines = Line + pBitmap->PixelsY / 8; while (Line < Lines) { pDestination = &IOMapDisplay.Display[Line * DISPLAY_WIDTH + pBitmap->StartX + pCord->StartX]; memcpy(pDestination,pSource,(size_t)pBitmap->PixelsX); pSource += pBitmap->PixelsX; Line++; } } } } void cDisplayCenterString(FONT *pFont,UBYTE *pString,UBYTE Line) { UWORD Chars; UBYTE Column; if (pString) { Chars = 0; while (pString[Chars]) { Chars++; } Column = (DISPLAY_WIDTH - Chars * pFont->ItemPixelsX) / 2; cDisplayString(pFont,Column,Line * 8,pString); } } void cDisplayUpdateMenuIcon(UBYTE *pIcon,SCREEN_CORDINATE *pCord) { UBYTE *pDestination; UBYTE Line; UBYTE Column; UBYTE Lines; UBYTE Columns; if (((pCord->StartY & 0x07) == 0) && ((pCord->PixelsY & 0x07) == 0)) { Line = pCord->StartY / 8; Lines = Line + pCord->PixelsY / 8; Columns = pCord->StartX + pCord->PixelsX; if (pIcon != NULL) { while (Line < Lines) { Column = pCord->StartX; pDestination = &IOMapDisplay.Display[Line * DISPLAY_WIDTH + Column]; while (Column < Columns) { *pDestination |= *pIcon; pIcon++; pDestination++; Column++; } Line++; } } else { while (Line < Lines) { pDestination = &IOMapDisplay.Display[Line * DISPLAY_WIDTH + pCord->StartX]; memset(pDestination,0,(size_t)pCord->PixelsX); Line++; } } } } void cDisplayUpdateIcon(ICON *pIcons,UBYTE Index,SCREEN_CORDINATE *pCord) { UBYTE *pSource; UBYTE *pDestination; UBYTE Line; UBYTE Lines; if (pIcons) { if ((Index > 0) && (Index <= (pIcons->ItemsX * pIcons->ItemsY))) { Index--; if (((pCord->StartY & 0x07) == 0) && ((pCord->PixelsY & 0x07) == 0)) { Line = pCord->StartY / 8; Lines = Line + pCord->PixelsY / 8; pSource = &pIcons->Data[((Index / pIcons->ItemsX) * pIcons->ItemsX * pIcons->ItemPixelsX * pIcons->ItemPixelsY / 8) + ((Index % pIcons->ItemsX) * pIcons->ItemPixelsX)]; while (Line < Lines) { pDestination = &IOMapDisplay.Display[Line * DISPLAY_WIDTH + pCord->StartX]; memcpy(pDestination,pSource,(size_t)pCord->PixelsX); pSource += (pIcons->ItemPixelsX * pIcons->ItemsX); Line++; } } } else { if (((pCord->StartY & 0x07) == 0) && ((pCord->PixelsY & 0x07) == 0)) { Line = pCord->StartY / 8; Lines = Line + pCord->PixelsY / 8; while (Line < Lines) { pDestination = &IOMapDisplay.Display[Line * DISPLAY_WIDTH + pCord->StartX]; memset(pDestination,0,(size_t)pCord->PixelsX); Line++; } } } } } void cDisplayLineX(UBYTE X1,UBYTE X2,UBYTE Y) { UBYTE X; UBYTE M; M = 1 << (Y % 8); Y >>= 3; for (X = X1;X < X2;X++) { IOMapDisplay.Display[Y * DISPLAY_WIDTH + X] |= M; } } void cDisplayLineY(UBYTE X,UBYTE Y1,UBYTE Y2) { UBYTE Y; for (Y = Y1;Y < Y2;Y++) { IOMapDisplay.Display[(Y / 8) * DISPLAY_WIDTH + X] |= (1 << (Y % 8)); } } void cDisplayFrame(SCREEN_CORDINATE *pCord) { cDisplayLineX(pCord->StartX,pCord->StartX + pCord->PixelsX - 1,pCord->StartY); cDisplayLineY(pCord->StartX,pCord->StartY,pCord->StartY + pCord->PixelsY - 1); cDisplayLineY(pCord->StartX + pCord->PixelsX - 1,pCord->StartY,pCord->StartY + pCord->PixelsY - 1); } void cDisplayEraseLine(UBYTE Line) { memset(&IOMapDisplay.Display[Line * DISPLAY_WIDTH], 0x00, DISPLAY_WIDTH); } void cDisplayErase(void) { memset(&IOMapDisplay.Display[0], 0x00, DISPLAY_WIDTH*DISPLAY_HEIGHT/8); } void cDisplayEraseScreen(SCREEN_CORDINATE *pCord) { UBYTE *pDestination; UBYTE Line; UBYTE Lines; if (((pCord->StartY & 0x07) == 0) && ((pCord->PixelsY & 0x07) == 0)) { Line = pCord->StartY / 8; Lines = Line + pCord->PixelsY / 8; while (Line < Lines) { pDestination = &IOMapDisplay.Display[Line * DISPLAY_WIDTH + pCord->StartX]; memset(pDestination,0,(size_t)pCord->PixelsX); Line++; } } } void cDisplayDraw(UBYTE Cmd,UBYTE On,UBYTE X1,UBYTE Y1,UBYTE X2,UBYTE Y2) { switch (Cmd) { case DISPLAY_ERASE_ALL : { cDisplayErase(); } break; case DISPLAY_PIXEL : { if (On == TRUE) { cDisplaySetPixel(X1,Y1); } else { cDisplayClrPixel(X1,Y1); } } break; case DISPLAY_HORISONTAL_LINE : { if (On == TRUE) { if (X1 > X2) { cDisplayLineX(X2,X1,Y1); } else { cDisplayLineX(X1,X2,Y1); } } } break; case DISPLAY_VERTICAL_LINE : { if (On == TRUE) { if (Y1 > Y2) { cDisplayLineY(X1,Y2,Y1); } else { cDisplayLineY(X1,Y1,Y2); } } } break; case DISPLAY_CHAR : { cDisplayChar(IOMapDisplay.pFont,On,X1,Y1,X2); } break; } } void cDisplayInit(void* pHeader) { dDisplayInit(); IOMapDisplay.Display = (UBYTE*)IOMapDisplay.Normal; IOMapDisplay.pFunc = &cDisplayDraw; IOMapDisplay.EraseMask = 0; IOMapDisplay.UpdateMask = 0; IOMapDisplay.TextLinesCenterFlags = 0; IOMapDisplay.Flags = DISPLAY_REFRESH | DISPLAY_ON; VarsDisplay.ErasePointer = 0; VarsDisplay.UpdatePointer = 0; } void cDisplayCtrl(void) { ULONG TmpMask; UBYTE Tmp; SCREEN_CORDINATE Cordinate; if (!(IOMapDisplay.Flags & DISPLAY_POPUP)) { if (IOMapDisplay.Display == (UBYTE*)IOMapDisplay.Popup) { IOMapDisplay.Display = VarsDisplay.DisplaySave; } } else { if (IOMapDisplay.Display != (UBYTE*)IOMapDisplay.Popup) { VarsDisplay.DisplaySave = IOMapDisplay.Display; IOMapDisplay.Display = (UBYTE*)IOMapDisplay.Popup; } } if (IOMapDisplay.EraseMask) { VarsDisplay.ErasePointer = 31; while ((VarsDisplay.ErasePointer) && (!(IOMapDisplay.EraseMask & (0x00000001 << VarsDisplay.ErasePointer)))) { VarsDisplay.ErasePointer--; } TmpMask = IOMapDisplay.EraseMask & (1 << VarsDisplay.ErasePointer); if ((TmpMask & TEXTLINE_BITS)) { Tmp = 0; while (!(TmpMask & TEXTLINE_BIT(Tmp))) { Tmp++; } if (Tmp < TEXTLINES) { cDisplayEraseLine(Tmp); } } else { if ((TmpMask & MENUICON_BITS)) { Tmp = 0; while (!(TmpMask & MENUICON_BIT(Tmp))) { Tmp++; } if (Tmp < MENUICONS) { cDisplayEraseScreen((SCREEN_CORDINATE*)&MENUICON_CORDINATES[Tmp]); } } else { if ((TmpMask & STATUSICON_BITS)) { Tmp = 0; while (!(TmpMask & STATUSICON_BIT(Tmp))) { Tmp++; } if (Tmp < STATUSICONS) { cDisplayEraseScreen((SCREEN_CORDINATE*)&STATUSICON_CORDINATES[Tmp]); } } else { if ((TmpMask & SCREEN_BITS)) { Tmp = 0; while (!(TmpMask & SCREEN_BIT(Tmp))) { Tmp++; } if (Tmp < SCREENS) { cDisplayEraseScreen((SCREEN_CORDINATE*)&SCREEN_CORDINATES[Tmp]); } if ((TmpMask & SCREEN_BIT(SCREEN_LARGE))) { if ((IOMapDisplay.UpdateMask & SPECIAL_BIT(TOPLINE))) { cDisplayLineX(0,DISPLAY_WIDTH - 1,9); IOMapDisplay.UpdateMask &= ~SPECIAL_BIT(TOPLINE); } } } else { if ((TmpMask & BITMAP_BITS)) { Tmp = 0; while (!(TmpMask & BITMAP_BIT(Tmp))) { Tmp++; } if (Tmp < BITMAPS) { Cordinate.StartX = VarsDisplay.pOldBitmaps[Tmp]->StartX; Cordinate.StartY = VarsDisplay.pOldBitmaps[Tmp]->StartY; Cordinate.PixelsX = VarsDisplay.pOldBitmaps[Tmp]->PixelsX; Cordinate.PixelsY = VarsDisplay.pOldBitmaps[Tmp]->PixelsY; cDisplayEraseScreen(&Cordinate); } } else { if ((TmpMask & SPECIAL_BITS)) { Tmp = 0; while (!(TmpMask & SPECIAL_BIT(Tmp))) { Tmp++; } switch (Tmp) { case FRAME_SELECT : { } break; case MENUTEXT : { cDisplayEraseLine(TEXTLINE_5); } break; case STATUSTEXT : { cDisplayEraseLine(TEXTLINE_1); } break; case STEPLINE : { } break; case TOPLINE : { } break; } } else { if ((TmpMask & STEPICON_BITS)) { Tmp = 0; while (!(TmpMask & STEPICON_BIT(Tmp))) { Tmp++; } if (Tmp < STEPICONS) { cDisplayEraseScreen((SCREEN_CORDINATE*)&STEPICON_CORDINATES[Tmp]); } } } } } } } } IOMapDisplay.EraseMask &= ~TmpMask; if (++VarsDisplay.ErasePointer >= 32) { VarsDisplay.ErasePointer = 0; } VarsDisplay.UpdatePointer = 0; } else { if (IOMapDisplay.UpdateMask) { VarsDisplay.UpdatePointer = 31; while ((VarsDisplay.UpdatePointer) && (!(IOMapDisplay.UpdateMask & (0x00000001 << VarsDisplay.UpdatePointer)))) { VarsDisplay.UpdatePointer--; } TmpMask = IOMapDisplay.UpdateMask & (0x00000001 << VarsDisplay.UpdatePointer); if ((TmpMask & TEXTLINE_BITS)) { Tmp = 0; while (!(TmpMask & TEXTLINE_BIT(Tmp))) { Tmp++; } if (Tmp < TEXTLINES) { if ((IOMapDisplay.TextLinesCenterFlags & (UBYTE)TmpMask)) { cDisplayCenterString(IOMapDisplay.pFont,IOMapDisplay.pTextLines[Tmp],TEXTLINE_1 + Tmp); } else { cDisplayString(IOMapDisplay.pFont,0,Tmp * 8,IOMapDisplay.pTextLines[Tmp]); } } } else { if ((TmpMask & MENUICON_BITS)) { Tmp = 0; while (!(TmpMask & MENUICON_BIT(Tmp))) { Tmp++; } if (Tmp < MENUICONS) { cDisplayUpdateMenuIcon(IOMapDisplay.pMenuIcons[Tmp],(SCREEN_CORDINATE*)&MENUICON_CORDINATES[Tmp]); } } else { if ((TmpMask & STATUSICON_BITS)) { Tmp = 0; while (!(TmpMask & STATUSICON_BIT(Tmp))) { Tmp++; } if (Tmp < STATUSICONS) { cDisplayUpdateIcon(IOMapDisplay.pStatusIcons,IOMapDisplay.StatusIcons[Tmp],(SCREEN_CORDINATE*)&STATUSICON_CORDINATES[Tmp]); } } else { if ((TmpMask & SCREEN_BITS)) { Tmp = 0; while (!(TmpMask & SCREEN_BIT(Tmp))) { Tmp++; } if (Tmp < SCREENS) { cDisplayUpdateScreen((SCREEN_CORDINATE*)&SCREEN_CORDINATES[Tmp],IOMapDisplay.pScreens[Tmp]); } } else { if ((TmpMask & BITMAP_BITS)) { Tmp = 0; while (!(TmpMask & BITMAP_BIT(Tmp))) { Tmp++; } if (Tmp < BITMAPS) { VarsDisplay.pOldBitmaps[Tmp] = IOMapDisplay.pBitmaps[Tmp]; cDisplayUpdateScreen((SCREEN_CORDINATE*)&SCREEN_CORDINATES[SCREEN_BACKGROUND],IOMapDisplay.pBitmaps[Tmp]); } } else { if ((TmpMask & SPECIAL_BITS)) { Tmp = 0; while (!(TmpMask & SPECIAL_BIT(Tmp))) { Tmp++; } switch (Tmp) { case FRAME_SELECT : { cDisplayFrame((SCREEN_CORDINATE*)&SELECT_FRAME_CORDINATES); } break; case MENUTEXT : { cDisplayCenterString(IOMapDisplay.pFont,IOMapDisplay.pMenuText,TEXTLINE_5); } break; case STATUSTEXT : { cDisplayCenterString(IOMapDisplay.pFont,IOMapDisplay.pStatusText,TEXTLINE_1); } break; case STEPLINE : { cDisplayLineX(22,28,20); cDisplayLineX(39,45,20); cDisplayLineX(56,62,20); cDisplayLineX(73,79,20); } break; case TOPLINE : { cDisplayLineX(0,DISPLAY_WIDTH - 1,9); } break; } } else { if ((TmpMask & STEPICON_BITS)) { Tmp = 0; while (!(TmpMask & STEPICON_BIT(Tmp))) { Tmp++; } if (Tmp < STEPICONS) { cDisplayUpdateIcon(IOMapDisplay.pStepIcons,IOMapDisplay.StepIcons[Tmp],(SCREEN_CORDINATE*)&STEPICON_CORDINATES[Tmp]); } } } } } } } } IOMapDisplay.TextLinesCenterFlags &= (UBYTE)(~TmpMask); IOMapDisplay.UpdateMask &= ~TmpMask; if (++VarsDisplay.UpdatePointer >= 32) { VarsDisplay.UpdatePointer = 0; } } VarsDisplay.ErasePointer = 0; } if (!(IOMapDisplay.Flags & DISPLAY_POPUP)) { if (!(IOMapDisplay.Flags & DISPLAY_REFRESH_DISABLED)) { if ((IOMapDisplay.Flags & DISPLAY_ON)) { dDisplayOn(TRUE); } else { dDisplayOn(FALSE); } if (!(dDisplayUpdate(DISPLAY_HEIGHT,DISPLAY_WIDTH,(UBYTE*)IOMapDisplay.Normal))) { IOMapDisplay.Flags &= ~DISPLAY_BUSY; if (!(IOMapDisplay.Flags & DISPLAY_REFRESH)) { IOMapDisplay.Flags |= DISPLAY_REFRESH_DISABLED; } } else { IOMapDisplay.Flags |= DISPLAY_BUSY; } } else { if ((IOMapDisplay.Flags & DISPLAY_REFRESH)) { IOMapDisplay.Flags &= ~DISPLAY_REFRESH_DISABLED; } } } else { dDisplayUpdate(DISPLAY_HEIGHT,DISPLAY_WIDTH,(UBYTE*)IOMapDisplay.Popup); } } void cDisplayExit(void) { dDisplayExit(); } nxt-firmware-1.29.7/src/c_display.h000066400000000000000000000015741466344546000171670ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_display.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_disp $ // // Platform C // #ifndef C_DISPLAY #define C_DISPLAY #ifndef INCLUDE_OS typedef struct { UBYTE *DisplaySave; BMPMAP *pOldBitmaps[BITMAPS]; UBYTE ErasePointer; UBYTE UpdatePointer; }VARSDISPLAY; #endif void cDisplayInit(void* pHeader); void cDisplayCtrl(void); void cDisplayExit(void); extern const HEADER cDisplay; #endif nxt-firmware-1.29.7/src/c_display.iom000066400000000000000000000164751466344546000175320ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_display.iom $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_disp $ // // Platform C // #ifndef CDISPLAY_IOM #define CDISPLAY_IOM #define pMapDisplay ((IOMAPDISPLAY*)(pHeaders[ENTRY_DISPLAY]->pIOMap)) // Constants related to simple draw entry (x = dont care) enum { DISPLAY_ERASE_ALL = 0x00, // W - erase entire screen (CMD,x,x,x,x,x) DISPLAY_PIXEL = 0x01, // W - set pixel (on/off) (CMD,TRUE/FALSE,X,Y,x,x) DISPLAY_HORISONTAL_LINE = 0x02, // W - draw horisontal line (CMD,TRUE,X1,Y1,X2,x) DISPLAY_VERTICAL_LINE = 0x03, // W - draw vertical line (CMD,TRUE,X1,Y1,x,Y2) DISPLAY_CHAR = 0x04 // W - draw char (actual font) (CMD,TRUE,X1,Y1,Char,x) }; // Constants related to Flags enum { DISPLAY_ON = 0x01, // W - Display on DISPLAY_REFRESH = 0x02, // W - Enable refresh DISPLAY_POPUP = 0x08, // W - Use popup display memory DISPLAY_REFRESH_DISABLED = 0x40, // R - Refresh disabled DISPLAY_BUSY = 0x80 // R - Refresh in progress }; #define DISPLAY_HEIGHT 64 // Y pixels #define DISPLAY_WIDTH 100 // X pixels #define DISPLAY_MENUICONS_Y 40 #define DISPLAY_MENUICONS_X_OFFS 7 #define DISPLAY_MENUICONS_X_DIFF 31 #define DISPLAY_IDLE ((pMapDisplay->EraseMask == 0) && (pMapDisplay->UpdateMask == 0)) enum TEXTLINE_NO // Used in macro "TEXTLINE_BIT" { TEXTLINE_1, // Upper most line TEXTLINE_2, // TEXTLINE_3, // TEXTLINE_4, // TEXTLINE_5, // TEXTLINE_6, // TEXTLINE_7, // TEXTLINE_8, // Buttom line TEXTLINES }; enum MENUICON_NO // Used in macro "MENUICON_BIT" { MENUICON_LEFT, // Left icon MENUICON_CENTER, // Center icon MENUICON_RIGHT, // Right icon MENUICONS }; enum SPECIAL_NO // Used in macro "SPECIAL_BIT" { FRAME_SELECT, // Center icon select frame STATUSTEXT, // Status text (BT name) MENUTEXT, // Center icon text STEPLINE, // Step collection lines TOPLINE, // Top status underline SPECIALS }; enum STATUSICON_NO // Used in macro "STATUSICON_BIT" { STATUSICON_BLUETOOTH, // BlueTooth status icon collection STATUSICON_USB, // USB status icon collection STATUSICON_VM, // VM status icon collection STATUSICON_BATTERY, // Battery status icon collection STATUSICONS }; enum SCREEN_NO // Used in macro "SCREEN_BIT" { SCREEN_BACKGROUND, // Entire screen SCREEN_LARGE, // Entire screen except status line SCREEN_SMALL, // Screen between menu icons and status line SCREENS }; enum BITMAP_NO // Used in macro "BITMAP_BIT" { BITMAP_1, // Bitmap 1 BITMAP_2, // Bitmap 2 BITMAP_3, // Bitmap 3 BITMAP_4, // Bitmap 4 BITMAPS }; enum STEP_NO // Used in macro "STEPICON_BIT" { STEPICON_1, // Left most step icon STEPICON_2, // STEPICON_3, // STEPICON_4, // STEPICON_5, // Right most step icon STEPICONS }; #define SCREEN_BITS ((ULONG)0xE0000000) // Executed as 1. #define STEPICON_BITS ((ULONG)0x1F000000) // Executed as 2. #define BITMAP_BITS ((ULONG)0x00F00000) // Executed as 3. #define MENUICON_BITS ((ULONG)0x000E0000) // Executed as 4. #define STATUSICON_BITS ((ULONG)0x0001E000) // Executed as 5. #define SPECIAL_BITS ((ULONG)0x00001F00) // Executed as 6. #define TEXTLINE_BITS ((ULONG)0x000000FF) // Executed as 7. #define SCREEN_BIT(No) ((ULONG)0x20000000 << (No)) #define STEPICON_BIT(No) ((ULONG)0x01000000 << (No)) #define BITMAP_BIT(No) ((ULONG)0x00100000 << (No)) #define MENUICON_BIT(No) ((ULONG)0x00020000 << (No)) #define STATUSICON_BIT(No) ((ULONG)0x00002000 << (No)) #define SPECIAL_BIT(No) ((ULONG)0x00000100 << (No)) #define TEXTLINE_BIT(No) ((ULONG)0x00000001 << (No)) typedef struct { void (*pFunc)(UBYTE,UBYTE,UBYTE,UBYTE,UBYTE,UBYTE); // Simple draw entry ULONG EraseMask; // Section erase mask (executed first) ULONG UpdateMask; // Section update mask (executed next) FONT *pFont; // Pointer to font file UBYTE *pTextLines[TEXTLINES]; // Pointer to text strings UBYTE *pStatusText; // Pointer to status text string ICON *pStatusIcons; // Pointer to status icon collection file BMPMAP *pScreens[SCREENS]; // Pointer to screen bitmap file BMPMAP *pBitmaps[BITMAPS]; // Pointer to free bitmap files UBYTE *pMenuText; // Pointer to menu icon text (NULL == none) UBYTE *pMenuIcons[MENUICONS]; // Pointer to menu icon images (NULL == none) ICON *pStepIcons; // Pointer to step icon collection file UBYTE *Display; // Display content copied to physical display every 17 mS UBYTE StatusIcons[STATUSICONS]; // Index in status icon collection file (index = 0 -> none) UBYTE StepIcons[STEPICONS]; // Index in step icon collection file (index = 0 -> none) UBYTE Flags; // Update flags enumerated above UBYTE TextLinesCenterFlags; // Mask to center TextLines UBYTE Normal[DISPLAY_HEIGHT / 8][DISPLAY_WIDTH]; // Raw display memory for normal screen UBYTE Popup[DISPLAY_HEIGHT / 8][DISPLAY_WIDTH]; // Raw display memory for popup screen } IOMAPDISPLAY; #endif nxt-firmware-1.29.7/src/c_input.c000066400000000000000000001116451466344546000166550ustar00rootroot00000000000000 // // Date init 14.12.2004 // // Revision date $Date:: 3/21/09 10:31a $ // // Filename $Workfile:: c_input.c $ // // Version $Revision:: 39 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_inpu $ // // Platform C // #include "stdconst.h" #include "modules.h" #include "c_input.h" #include "d_input.h" #include "c_output.iom" #include "c_loader.iom" #include #define INVALID_RELOAD_NORMAL 20 #define INVALID_RELOAD_SOUND 300 #define INVALID_RELOAD_COLOR 400 #define ROT_SLOW_SPEED 30 #define ROT_OV_SAMPLING 7 #define VCC_SENSOR 5000L #define VCC_SENSOR_DIODE 4300L #define AD_MAX 1023L #define REFLECTIONSENSORMIN (1906L/(VCC_SENSOR/AD_MAX)) #define REFLECTIONSENSORMAX ((AD_MAX * 4398L)/VCC_SENSOR) #define REFLECTIONSENSORPCTDYN (UBYTE)(((REFLECTIONSENSORMAX - REFLECTIONSENSORMIN) * 100L)/AD_MAX) #define NEWLIGHTSENSORMIN (800L/(VCC_SENSOR/AD_MAX)) #define NEWLIGHTSENSORMAX ((AD_MAX * 4400L)/VCC_SENSOR) #define NEWLIGHTSENSORPCTDYN (UBYTE)(((NEWLIGHTSENSORMAX - NEWLIGHTSENSORMIN) * 100L)/AD_MAX) #define NEWSOUNDSENSORMIN (650L/(VCC_SENSOR/AD_MAX)) #define NEWSOUNDSENSORMAX ((AD_MAX * 4980L)/VCC_SENSOR) #define NEWSOUNDSENSORPCTDYN (UBYTE)(((NEWSOUNDSENSORMAX - NEWSOUNDSENSORMIN) * 100L)/AD_MAX) /* Remember this is ARM AD converter - 3,3 VDC as max voltage */ /* When in color mode background value is substracted => min = 0!!! */ #define COLORSENSORBGMIN (214/(3300/AD_MAX)) #define COLORSENSORMIN (1L/(3300/AD_MAX)) /* 1 inserted else div 0 (1L/(120/AD_MAX)) */ #define COLORSENSORMAX ((AD_MAX * 3300L)/3300) #define COLORSENSORPCTDYN (UBYTE)(((COLORSENSORMAX - COLORSENSORMIN) * 100L)/AD_MAX) #define COLORSENSORBGPCTDYN (UBYTE)(((COLORSENSORMAX - COLORSENSORBGMIN) * 100L)/AD_MAX) enum { POWER = 0x00, NO_POWER = 0x01, ACTIVE = 0x02, ALWAYS_ACTIVE = 0x04, DIGI_0_HIGH = 0x08, DIGI_1_HIGH = 0x10, DIGI_0_IN = 0x20, DIGI_1_IN = 0x40, CUSTOM_SETUP = 0x80 }; static IOMAPINPUT IOMapInput; static VARSINPUT VarsInput; const HEADER cInput = { 0x00030001L, "Input", cInputInit, cInputCtrl, cInputExit, (void *)&IOMapInput, (void *)&VarsInput, (UWORD)sizeof(IOMapInput), (UWORD)sizeof(VarsInput), 0x0000 //Code size - not used so far }; void cInputCalcFullScale(UWORD *pRawVal, UWORD ZeroPointOffset, UBYTE PctFullScale, UBYTE InvState); void cInputCalcSensorValue(UWORD NewSensorRaw, UWORD *pOldSensorRaw, SWORD *pSensorValue, UBYTE *pBoolean, UBYTE *pDebounce, UBYTE *pSampleCnt, UBYTE *LastAngle, UBYTE *pEdgeCnt, UBYTE Slope, UBYTE Mode); void cInputSetupType(UBYTE Port, UBYTE *pType, UBYTE OldType); void cInputSetupCustomSensor(UBYTE Port); void cInputCalcSensorValues(UBYTE No); UBYTE cInputInitColorSensor(UBYTE Port, UBYTE *pInitStatus); void cInputCalibrateColor(COLORSTRUCT *pC, UWORD *pNewVals); SWORD cInputTempConv(UWORD InputVal); void cInputInit(void* pHeader) { UBYTE Tmp; memset(IOMapInput.Colors, 0, sizeof(IOMapInput.Colors)); memset(VarsInput.VarsColor, 0, sizeof(VarsInput.VarsColor)); /* Init IO map */ for (Tmp = 0; Tmp < NO_OF_INPUTS; Tmp++) { IOMapInput.Inputs[Tmp].SensorType = NO_SENSOR; IOMapInput.Inputs[Tmp].SensorMode = RAWMODE; IOMapInput.Inputs[Tmp].SensorRaw = 0; IOMapInput.Inputs[Tmp].SensorValue = 0; IOMapInput.Inputs[Tmp].SensorBoolean = 0; IOMapInput.Inputs[Tmp].InvalidData = INVALID_DATA; IOMapInput.Inputs[Tmp].DigiPinsDir = 0; IOMapInput.Inputs[Tmp].DigiPinsOut = 0; IOMapInput.Inputs[Tmp].CustomActiveStatus = CUSTOMINACTIVE; IOMapInput.Inputs[Tmp].CustomZeroOffset = 0; IOMapInput.Inputs[Tmp].CustomPctFullScale = 0; dInputRead0(Tmp, &(IOMapInput.Inputs[Tmp].DigiPinsIn)); dInputRead1(Tmp, &(IOMapInput.Inputs[Tmp].DigiPinsIn)); VarsInput.EdgeCnt[Tmp] = 0; VarsInput.InputDebounce[Tmp] = 0; VarsInput.LastAngle[Tmp] = 0; VarsInput.SampleCnt[Tmp] = 0; VarsInput.InvalidTimer[Tmp] = INVALID_RELOAD_NORMAL; VarsInput.OldSensorType[Tmp] = NO_SENSOR; } VarsInput.ColorStatus = 0; VarsInput.ColorCnt = 0; dInputInit(); } void cInputCtrl(void) { UBYTE Tmp; if (VarsInput.ColorStatus) { switch(VarsInput.ColorCnt) { case 0: { VarsInput.ColorCnt = 1; dInputSetColorClkInput(); } break; case 1: { VarsInput.ColorCnt = 2; } break; case 2: { VarsInput.ColorCnt = 0; dInputGetAllColors(IOMapInput.Colors, VarsInput.ColorStatus); } break; default: { VarsInput.ColorCnt = 0; } break; } } for (Tmp = 0; Tmp < NO_OF_INPUTS; Tmp++) { UBYTE sType = IOMapInput.Inputs[Tmp].SensorType; UBYTE *pType = &IOMapInput.Inputs[Tmp].SensorType; UBYTE oldType = VarsInput.OldSensorType[Tmp]; if (sType != oldType) { /* Clear all variables for this sensor */ VarsInput.EdgeCnt[Tmp] = 0; VarsInput.InputDebounce[Tmp] = 0; VarsInput.LastAngle[Tmp] = 0; VarsInput.SampleCnt[Tmp] = 0; VarsInput.ColorStatus &= ~(0x01< 928) InputVal = 928; InputVal = cInputTempConv(InputVal - 290); InputVal = InputVal + 200; InputVal = (UWORD)(((SLONG)InputVal * (SLONG)1023)/(SLONG)900); } else if (sType == LIGHT_ACTIVE || sType == LIGHT_INACTIVE) { cInputCalcFullScale(&InputVal, NEWLIGHTSENSORMIN, NEWLIGHTSENSORPCTDYN, TRUE); } else if (sType == SOUND_DB || sType == SOUND_DBA) { cInputCalcFullScale(&InputVal, NEWSOUNDSENSORMIN, NEWSOUNDSENSORPCTDYN, TRUE); } else if (sType == CUSTOM) { cInputCalcFullScale(&InputVal, IOMapInput.Inputs[No].CustomZeroOffset, IOMapInput.Inputs[No].CustomPctFullScale, FALSE); } cInputCalcSensorValue( InputVal, &(IOMapInput.Inputs[No].SensorRaw), &(IOMapInput.Inputs[No].SensorValue), &(IOMapInput.Inputs[No].SensorBoolean), &(VarsInput.InputDebounce[No]), &(VarsInput.SampleCnt[No]), &(VarsInput.LastAngle[No]), &(VarsInput.EdgeCnt[No]), ((IOMapInput.Inputs[No].SensorMode) & SLOPEMASK), ((IOMapInput.Inputs[No].SensorMode) & MODEMASK)); } break; /* Tripple case intended */ case LOWSPEED: case LOWSPEED_9V: case HIGHSPEED: { } break; /* Four cases intended */ case COLORRED: case COLORGREEN: case COLORBLUE: case COLORNONE: { UWORD InputVal; switch (IOMapInput.Colors[No].CalibrationState) { case SENSOROFF: { /* Make sure that sensor data are invalid while unplugged*/ VarsInput.InvalidTimer[No] = INVALID_RELOAD_COLOR; IOMapInput.Inputs[No].InvalidData = INVALID_DATA; /* Check if sensor has been attached */ if (dInputCheckColorStatus(No)) { /* Sensor has been attached now get cal data */ VarsInput.VarsColor[No].ColorInitState = 0; (IOMapInput.Colors[No].CalibrationState) = SENSORCAL; } } break; case SENSORCAL: { UBYTE Status; if (FALSE == cInputInitColorSensor(No, &Status)) { /* Color sensor has been removed during calibration */ (IOMapInput.Colors[No].CalibrationState) = SENSOROFF; } if (TRUE == Status) { /* Use clock to detect errors */ dInputSetDirInDigi0(No); (IOMapInput.Colors[No].CalibrationState) = 0; } } break; default: { if (dInputGetColor(No, &(IOMapInput.Inputs[No].ADRaw))) { InputVal = IOMapInput.Inputs[No].ADRaw; cInputCalcFullScale(&InputVal, COLORSENSORBGMIN, COLORSENSORBGPCTDYN, FALSE); cInputCalcSensorValue(InputVal, &(IOMapInput.Inputs[No].SensorRaw), &(IOMapInput.Inputs[No].SensorValue), &(IOMapInput.Inputs[No].SensorBoolean), &(VarsInput.InputDebounce[No]), &(VarsInput.SampleCnt[No]), &(VarsInput.LastAngle[No]), &(VarsInput.EdgeCnt[No]), ((IOMapInput.Inputs[No].SensorMode) & SLOPEMASK), ((IOMapInput.Inputs[No].SensorMode) & MODEMASK)); } else { IOMapInput.Colors[No].CalibrationState = SENSOROFF; } } break; } } break; case COLORFULL: { switch (IOMapInput.Colors[No].CalibrationState) { case SENSOROFF: { /* Make sure that sensor data are invalid while unplugged */ VarsInput.InvalidTimer[No] = INVALID_RELOAD_COLOR; IOMapInput.Inputs[No].InvalidData = INVALID_DATA; /* Check if sensor has been attached */ if (dInputCheckColorStatus(No)) { /* Sensor has been attached now get cal data */ VarsInput.VarsColor[No].ColorInitState = 0; (IOMapInput.Colors[No].CalibrationState) = SENSORCAL; } } break; case SENSORCAL: { UBYTE Status; if (FALSE == cInputInitColorSensor(No, &Status)) { /* Color sensor has been removed during calibration */ (IOMapInput.Colors[No].CalibrationState) = SENSOROFF; VarsInput.ColorStatus &= ~(0x01<SensorRaw[RED]) > (pC->SensorRaw[BLUE] )) && ((pC->SensorRaw[RED]) > (pC->SensorRaw[GREEN]))) { /* If all 3 colors are less than 65 OR (Less that 110 and bg less than 40)*/ if (((pC->SensorRaw[RED]) < 65) || (((pC->SensorRaw[BLANK]) < 40) && ((pC->SensorRaw[RED]) < 110))) { IOMapInput.Inputs[No].SensorValue = BLACKCOLOR; } else { if (((((pC->SensorRaw[BLUE]) >> 2) + ((pC->SensorRaw[BLUE]) >> 3) + (pC->SensorRaw[BLUE])) < (pC->SensorRaw[GREEN])) && ((((pC->SensorRaw[GREEN]) << 1)) > (pC->SensorRaw[RED]))) { IOMapInput.Inputs[No].SensorValue = YELLOWCOLOR; } else { if ((((pC->SensorRaw[GREEN]) << 1) - ((pC->SensorRaw[GREEN]) >> 2)) < (pC->SensorRaw[RED])) { IOMapInput.Inputs[No].SensorValue = REDCOLOR; } else { if ((((pC->SensorRaw[BLUE]) < 70) || ((pC->SensorRaw[GREEN]) < 70)) || (((pC->SensorRaw[BLANK]) < 140) && ((pC->SensorRaw[RED]) < 140))) { IOMapInput.Inputs[No].SensorValue = BLACKCOLOR; } else { IOMapInput.Inputs[No].SensorValue = WHITECOLOR; } } } } } else { /* Red is not the dominant color */ if ((pC->SensorRaw[GREEN]) > (pC->SensorRaw[BLUE])) { /* Green is the dominant color */ /* If all 3 colors are less than 40 OR (Less that 70 and bg less than 20)*/ if (((pC->SensorRaw[GREEN]) < 40) || (((pC->SensorRaw[BLANK]) < 30) && ((pC->SensorRaw[GREEN]) < 70))) { IOMapInput.Inputs[No].SensorValue = BLACKCOLOR; } else { if ((((pC->SensorRaw[BLUE]) << 1)) < (pC->SensorRaw[RED])) { IOMapInput.Inputs[No].SensorValue = YELLOWCOLOR; } else { if ((((pC->SensorRaw[RED]) + ((pC->SensorRaw[RED])>>2)) < (pC->SensorRaw[GREEN])) || (((pC->SensorRaw[BLUE]) + ((pC->SensorRaw[BLUE])>>2)) < (pC->SensorRaw[GREEN]))) { IOMapInput.Inputs[No].SensorValue = GREENCOLOR; } else { if ((((pC->SensorRaw[RED]) < 70) || ((pC->SensorRaw[BLUE]) < 70)) || (((pC->SensorRaw[BLANK]) < 140) && ((pC->SensorRaw[GREEN]) < 140))) { IOMapInput.Inputs[No].SensorValue = BLACKCOLOR; } else { IOMapInput.Inputs[No].SensorValue = WHITECOLOR; } } } } } else { /* Blue is the most dominant color */ /* Colors can be blue, white or black */ /* If all 3 colors are less than 48 OR (Less that 85 and bg less than 25)*/ if (((pC->SensorRaw[BLUE]) < 48) || (((pC->SensorRaw[BLANK]) < 25) && ((pC->SensorRaw[BLUE]) < 85))) { IOMapInput.Inputs[No].SensorValue = BLACKCOLOR; } else { if ((((((pC->SensorRaw[RED]) * 48) >> 5) < (pC->SensorRaw[BLUE])) && ((((pC->SensorRaw[GREEN]) * 48) >> 5) < (pC->SensorRaw[BLUE]))) || (((((pC->SensorRaw[RED]) * 58) >> 5) < (pC->SensorRaw[BLUE])) || ((((pC->SensorRaw[GREEN]) * 58) >> 5) < (pC->SensorRaw[BLUE])))) { IOMapInput.Inputs[No].SensorValue = BLUECOLOR; } else { /* Color is white or Black */ if ((((pC->SensorRaw[RED]) < 60) || ((pC->SensorRaw[GREEN]) < 60)) || (((pC->SensorRaw[BLANK]) < 110) && ((pC->SensorRaw[BLUE]) < 120))) { IOMapInput.Inputs[No].SensorValue = BLACKCOLOR; } else { if ((((pC->SensorRaw[RED]) + ((pC->SensorRaw[RED]) >> 3)) < (pC->SensorRaw[BLUE])) || (((pC->SensorRaw[GREEN]) + ((pC->SensorRaw[GREEN]) >> 3)) < (pC->SensorRaw[BLUE]))) { IOMapInput.Inputs[No].SensorValue = BLUECOLOR; } else { IOMapInput.Inputs[No].SensorValue = WHITECOLOR; } } } } } } } else { IOMapInput.Colors[No].CalibrationState = SENSOROFF; VarsInput.ColorStatus &= ~(0x01< THRESHOLD_FALSE) { PresentBoolean = FALSE; } else { if (NewSensorRaw < THRESHOLD_TRUE) { PresentBoolean = TRUE; } } } else { /* This is dynamic measure method */ if (NewSensorRaw > (ACTUAL_AD_RES - Slope)) { PresentBoolean = FALSE; } else { if (NewSensorRaw < Slope) { PresentBoolean = TRUE; } else { Delta = *pOldSensorRaw - NewSensorRaw; if (Delta < 0) { if (-Delta > Slope) { PresentBoolean = FALSE; } } else { if (Delta > Slope) { PresentBoolean = TRUE; } } } } } *pOldSensorRaw = NewSensorRaw; switch(Mode) { case RAWMODE: { *pSensorValue = NewSensorRaw; } break; case BOOLEANMODE: { *pSensorValue = PresentBoolean; } break; case TRANSITIONCNTMODE: { if ((*pDebounce) > 0) { (*pDebounce)--; } else { if (*pBoolean != PresentBoolean) { (*pDebounce) = DEBOUNCERELOAD; (*pSensorValue)++; } } } break; case PERIODCOUNTERMODE: { if ((*pDebounce) > 0) { (*pDebounce)--; } else { if (*pBoolean != PresentBoolean) { (*pDebounce) = DEBOUNCERELOAD; *pBoolean = PresentBoolean; if (++(*pEdgeCnt) > 1) { if (PresentBoolean == 0) { (*pEdgeCnt) = 0; (*pSensorValue)++; } } } } } break; case PCTFULLSCALEMODE: { /* Output is 0-100 pct */ *pSensorValue = ((NewSensorRaw) * 100)/SENSOR_RESOLUTION; } break; case FAHRENHEITMODE: { /* Fahrenheit mode goes from -40 to 158 degrees */ *pSensorValue = (((ULONG)(NewSensorRaw) * 900L)/SENSOR_RESOLUTION) - 200; *pSensorValue = ((180L * (ULONG)(*pSensorValue))/100L) + 320; } break; case CELSIUSMODE: { /* Celsius mode goes from -20 to 70 degrees */ *pSensorValue = (((ULONG)(NewSensorRaw * 900L)/SENSOR_RESOLUTION) - 200); } break; case ANGLESTEPSMODE: { *pBoolean = PresentBoolean; if (NewSensorRaw < ANGLELIMITA) { Sample = 0; } else { if (NewSensorRaw < ANGLELIMITB) { Sample = 1; } else { if (NewSensorRaw < ANGLELIMITC) { Sample = 2; } else { Sample = 3; } } } switch (*LastAngle) { case 0 : { if (Sample == 1) { if ((*pSampleCnt) >= ROT_SLOW_SPEED ) { if (++(*pSampleCnt) >= (ROT_SLOW_SPEED + ROT_OV_SAMPLING)) { (*pSensorValue)++; (*LastAngle) = Sample; } } else { (*pSensorValue)++; (*LastAngle) = Sample; } } if (Sample == 2) { (*pSensorValue)--; (*LastAngle) = Sample; } if (Sample == 0) { if ((*pSampleCnt) < ROT_SLOW_SPEED) { (*pSampleCnt)++; } } } break; case 1 : { if (Sample == 3) { (*pSensorValue)++; (*LastAngle) = Sample; } if (Sample == 0) { (*pSensorValue)--; (*LastAngle) = Sample; } (*pSampleCnt) = 0; } break; case 2 : { if (Sample == 0) { (*pSensorValue)++; (*LastAngle) = Sample; } if (Sample == 3) { (*pSensorValue)--; (*LastAngle) = Sample; } (*pSampleCnt) = 0; } break; case 3 : { if (Sample == 2) { if ((*pSampleCnt) >= ROT_SLOW_SPEED) { if (++(*pSampleCnt) >= (ROT_SLOW_SPEED + ROT_OV_SAMPLING)) { (*pSensorValue)++; (*LastAngle) = Sample; } } else { (*pSensorValue)++; (*LastAngle) = Sample; } } if (Sample == 1) { (*pSensorValue)--; (*LastAngle) = Sample; } if (Sample == 3) { if ((*pSampleCnt) < ROT_SLOW_SPEED) { (*pSampleCnt)++; } } } break; } } } *pBoolean = PresentBoolean; } void cInputCalcFullScale(UWORD *pRawVal, UWORD ZeroPointOffset, UBYTE PctFullScale, UBYTE InvStatus) { if (*pRawVal >= ZeroPointOffset) { *pRawVal -= ZeroPointOffset; } else { *pRawVal = 0; } *pRawVal = (*pRawVal * 100)/PctFullScale; if (*pRawVal > SENSOR_RESOLUTION) { *pRawVal = SENSOR_RESOLUTION; } if (TRUE == InvStatus) { *pRawVal = SENSOR_RESOLUTION - *pRawVal; } } void cInputSetupType(UBYTE Port, UBYTE *pType, UBYTE OldType) { VarsInput.InvalidTimer[Port] = INVALID_RELOAD_NORMAL; /* If old type is color sensor in color lamp mode then turn off leds */ switch (OldType) { case COLORRED: case COLORGREEN: case COLORBLUE: case COLORFULL: case COLOREXIT: { if (NO_SENSOR == *pType) { VarsInput.InvalidTimer[Port] = INVALID_RELOAD_COLOR; *pType = COLOREXIT; } } break; } switch(*pType) { case NO_SENSOR: case SWITCH: case TEMPERATURE: { dInputSetInactive(Port); dInputSetDirInDigi0(Port); dInputSetDirInDigi1(Port); } break; case REFLECTION: case ANGLE: { dInputSetActive(Port); dInputClearDigi0(Port); dInputClearDigi1(Port); } break; case LIGHT_ACTIVE: { dInputSetInactive(Port); dInputSetDigi0(Port); dInputClearDigi1(Port); } break; case LIGHT_INACTIVE: { dInputSetInactive(Port); dInputClearDigi0(Port); dInputClearDigi1(Port); } break; case SOUND_DB: { VarsInput.InvalidTimer[Port] = INVALID_RELOAD_SOUND; dInputSetInactive(Port); dInputSetDigi0(Port); dInputClearDigi1(Port); } break; case SOUND_DBA: { VarsInput.InvalidTimer[Port] = INVALID_RELOAD_SOUND; dInputSetInactive(Port); dInputClearDigi0(Port); dInputSetDigi1(Port); } break; case CUSTOM: { cInputSetupCustomSensor(Port); } break; case LOWSPEED: { dInputSetInactive(Port); dInputSetDigi0(Port); dInputSetDigi1(Port); } break; case LOWSPEED_9V: { dInputSet9v(Port); dInputSetDigi0(Port); dInputSetDigi1(Port); } break; case HIGHSPEED: { dInputSetInactive(Port); dInputSetDirInDigi0(Port); dInputSetDirInDigi1(Port); } break; case COLORFULL: case COLORRED: case COLORGREEN: case COLORBLUE: case COLORNONE: { VarsInput.InvalidTimer[Port] = INVALID_RELOAD_COLOR; dInputSetInactive(Port); dInputSetDigi0(Port); dInputSetDirInDigi1(Port); IOMapInput.Colors[Port].CalibrationState = SENSORCAL; VarsInput.VarsColor[Port].ColorInitState = 0; } break; default: { } break; } } void cInputSetupCustomSensor(UBYTE Port) { if ((IOMapInput.Inputs[Port].DigiPinsDir) & 0x01) { if ((IOMapInput.Inputs[Port].DigiPinsOut) & 0x01) { dInputSetDigi0(Port); } else { dInputClearDigi0(Port); } } if ((IOMapInput.Inputs[Port].DigiPinsDir) & 0x02) { if ((IOMapInput.Inputs[Port].DigiPinsOut) & 0x02) { dInputSetDigi1(Port); } else { dInputClearDigi1(Port); } } else { dInputSetDirInDigi1(Port); } if (CUSTOMACTIVE == (IOMapInput.Inputs[Port].CustomActiveStatus)) { dInputSetActive(Port); } else { if (CUSTOM9V == (IOMapInput.Inputs[Port].CustomActiveStatus)) { dInputSet9v(Port); } else { dInputSetInactive(Port); } } } SWORD cInputTempConv(UWORD InputVal) { static const long long TempCoeff[] = { -5425ll, 9261399ll, -6686663252ll, 2573629857807ll, -822478326197838ll, 195856762719738784ll }; const unsigned int TempCoeffShift = 48; /* Replace the original table with polynomial. */ unsigned i; long long Input = InputVal; long long Output = TempCoeff[0]; for (i = 1; i < sizeof TempCoeff / sizeof TempCoeff[0]; i++) Output = Output * Input + TempCoeff[i]; /* Round. */ return (Output + (1ll << (TempCoeffShift - 1))) >> TempCoeffShift; } UBYTE cInputInitColorSensor(UBYTE Port, UBYTE *pInitStatus) { *pInitStatus = FALSE; switch(VarsInput.VarsColor[Port].ColorInitState) { case 0: { dInputSetDigi0(Port); dInputSetDigi1(Port); VarsInput.VarsColor[Port].ColorInitState++; } break; case 1: { dInputClearDigi0(Port); VarsInput.VarsColor[Port].ColorInitState++; } break; case 2: { dInputSetDigi0(Port); VarsInput.VarsColor[Port].ColorInitState++; } break; case 3: { dInputClearDigi0(Port); /* Clear clock for 100mS - use pit timer*/ dInputClearColor100msTimer(Port); VarsInput.VarsColor[Port].ColorInitState++; } break; case 4: { /* Wait 100mS */ if (dInputChkColor100msTimer(Port)) { VarsInput.VarsColor[Port].ColorInitState += 1; } } break; case 5: { UBYTE TmpType; if (COLOREXIT == IOMapInput.Inputs[Port].SensorType) { TmpType = COLORNONE; } else { TmpType = IOMapInput.Inputs[Port].SensorType; } dInputColorTx(Port, TmpType); /* Be ready to receive data from sensor */ dInputSetDirInDigi1(Port); VarsInput.VarsColor[Port].ReadCnt = 0; VarsInput.VarsColor[Port].ColorInitState++; } break; case 6: { UBYTE Data; UBYTE DataCnt; UBYTE *pData; DataCnt = (VarsInput.VarsColor[Port].ReadCnt); pData = (UBYTE*)(IOMapInput.Colors[Port].Calibration); /* Read first byte of cal data */ dInputReadCal(Port, &Data); pData[DataCnt] = Data; /* If all bytes has been read - then continue to next step */ if (++(VarsInput.VarsColor[Port].ReadCnt) >= ((sizeof(IOMapInput.Colors[Port].Calibration) + sizeof(IOMapInput.Colors[Port].CalLimits)))) { VarsInput.VarsColor[Port].ColorInitState++; } } break; case 7: { /* Check CRC then continue or restart if false */ UWORD Crc, CrcCheck; UBYTE Cnt; UBYTE Data; UBYTE *pData; dInputReadCal(Port, &Data); Crc = (UWORD)(Data) << 8; dInputReadCal(Port, &Data); Crc += (UWORD)Data; CrcCheck = 0x5AA5; pData = (UBYTE*)(IOMapInput.Colors[Port].Calibration); for (Cnt = 0; Cnt < (sizeof(IOMapInput.Colors[Port].Calibration) + sizeof(IOMapInput.Colors[Port].CalLimits)); Cnt++) { UWORD i,j; UBYTE c; c = pData[Cnt]; for(i = 0; i != 8; c >>= 1, i++) { j = (c^CrcCheck) & 1; CrcCheck >>= 1; if(j) { CrcCheck ^= 0xA001; } } } if ((CrcCheck != Crc)) { /* incorrect!!! try again */ VarsInput.VarsColor[Port].ColorInitState = 0; VarsInput.InvalidTimer[Port] = INVALID_RELOAD_COLOR; } else { /* Correct crc sum -> calculate the calibration values then exit */ VarsInput.VarsColor[Port].ColorInitState = 0; /* Sensor is almost ready - needs a little time to make first measurements */ VarsInput.InvalidTimer[Port] = 10; *pInitStatus = TRUE; } } break; default: { VarsInput.VarsColor[Port].ColorInitState = 0; } break; } return(dInputCheckColorStatus(Port)); } void cInputCalibrateColor(COLORSTRUCT *pC, UWORD *pNewVals) { UBYTE CalRange; if ((pC->ADRaw[BLANK]) < pC->CalLimits[1]) { CalRange = 2; } else { if ((pC->ADRaw[BLANK]) < pC->CalLimits[0]) { CalRange = 1; } else { CalRange = 0; } } pNewVals[RED] = 0; if ((pC->ADRaw[RED]) > (pC->ADRaw[BLANK])) { pNewVals[RED] = (UWORD)(((ULONG)((pC->ADRaw[RED]) - (pC->ADRaw[BLANK])) * (pC->Calibration[CalRange][RED])) >> 16); } pNewVals[GREEN] = 0; if ((pC->ADRaw[GREEN]) > (pC->ADRaw[BLANK])) { pNewVals[GREEN] = (UWORD)(((ULONG)((pC->ADRaw[GREEN]) - (pC->ADRaw[BLANK])) * (pC->Calibration[CalRange][GREEN])) >> 16); } pNewVals[BLUE] = 0; if ((pC->ADRaw[BLUE]) > (pC->ADRaw[BLANK])) { pNewVals[BLUE] = (UWORD)(((ULONG)((pC->ADRaw[BLUE]) -(pC->ADRaw[BLANK])) * (pC->Calibration[CalRange][BLUE])) >> 16); } pNewVals[BLANK] = (pC->ADRaw[BLANK]); cInputCalcFullScale(&(pNewVals[BLANK]), COLORSENSORBGMIN, COLORSENSORBGPCTDYN, FALSE); (pNewVals[BLANK]) = (UWORD)(((ULONG)(pNewVals[BLANK]) * (pC->Calibration[CalRange][BLANK])) >> 16); } void cInputExit(void) { dInputExit(); } nxt-firmware-1.29.7/src/c_input.h000066400000000000000000000036611466344546000166600ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-01-09 10:33 $ // // Filename $Workfile:: c_input.h $ // // Version $Revision:: 7 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_inpu $ // // Platform C // #ifndef C_INPUT #define C_INPUT #ifdef INCLUDE_OS extern const HEADER cInput; #endif #include "c_input.iom" #define ACTUAL_AD_RES 1023L #define SENSOR_RESOLUTION 1023L #define DEBOUNCERELOAD 100 #define THRESHOLD_FALSE (UWORD)(ACTUAL_AD_RES * 45L / 100L) #define THRESHOLD_TRUE (UWORD)(ACTUAL_AD_RES * 55L / 100L) #define ANGLELIMITA (UWORD)(ACTUAL_AD_RES * 4400L / 10000L) #define ANGLELIMITB (UWORD)(ACTUAL_AD_RES * 6600L / 10000L) #define ANGLELIMITC (UWORD)(ACTUAL_AD_RES * 8900L / 10000L) #define FWDDIR 1 #define RWDDIR 2 #define MAXSAMPLECNT 5 typedef struct { UBYTE ColorInputDebounce [NO_OF_COLORS]; UBYTE ColorEdgeCnt [NO_OF_COLORS]; UBYTE ColorLastAngle [NO_OF_COLORS]; UBYTE ColorSampleCnt [NO_OF_COLORS]; UBYTE ColorInitState; UBYTE ReadCnt; } VARSCOLOR; typedef struct { UWORD InvalidTimer [NO_OF_INPUTS]; UBYTE InputDebounce [NO_OF_INPUTS]; UBYTE EdgeCnt [NO_OF_INPUTS]; UBYTE LastAngle [NO_OF_INPUTS]; UBYTE OldSensorType [NO_OF_INPUTS]; UBYTE SampleCnt [NO_OF_INPUTS]; VARSCOLOR VarsColor [NO_OF_INPUTS]; UBYTE ColorCnt; UBYTE ColorStatus; }VARSINPUT; void cInputInit(void* pHeader); void cInputCtrl(void); void cInputExit(void); #endif nxt-firmware-1.29.7/src/c_input.iom000066400000000000000000000076121466344546000172150ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 24-09-08 15:23 $ // // Filename $Workfile:: c_input.iom $ // // Version $Revision:: 16 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_inpu $ // // Platform C // #ifndef CINPUT_IOM #define CINPUT_IOM #define NO_OF_INPUTS 4 #define pMapInput ((IOMAPINPUT*)(pHeaders[ENTRY_INPUT]->pIOMap)) /* Constants related to sensor type */ enum { NO_SENSOR = 0, SWITCH = 1, TEMPERATURE = 2, REFLECTION = 3, ANGLE = 4, LIGHT_ACTIVE = 5, LIGHT_INACTIVE = 6, SOUND_DB = 7, SOUND_DBA = 8, CUSTOM = 9, LOWSPEED = 10, LOWSPEED_9V = 11, HIGHSPEED = 12, COLORFULL = 13, COLORRED = 14, COLORGREEN = 15, COLORBLUE = 16, COLORNONE = 17, COLOREXIT = 18, /* For internal use when going from color or Lamp to no_sensor*/ NO_OF_SENSOR_TYPES = 18 }; /* Constants related to sensor mode */ enum { RAWMODE = 0x00, BOOLEANMODE = 0x20, TRANSITIONCNTMODE = 0x40, PERIODCOUNTERMODE = 0x60, PCTFULLSCALEMODE = 0x80, CELSIUSMODE = 0xA0, FAHRENHEITMODE = 0xC0, ANGLESTEPSMODE = 0xE0, SLOPEMASK = 0x1F, MODEMASK = 0xE0 }; /* Constants related to Digital I/O */ enum { DIGI0 = 1, DIGI1 = 2 }; enum { CUSTOMINACTIVE = 0x00, CUSTOM9V = 0x01, CUSTOMACTIVE = 0x02 }; enum { INVALID_DATA = 0x01 }; /* Constants related to Colorstruct */ enum { RED, GREEN, BLUE, BLANK, NO_OF_COLORS }; /* Constants related to color sensor value using */ /* Color sensor as color detector */ enum { BLACKCOLOR = 1, BLUECOLOR = 2, GREENCOLOR = 3, YELLOWCOLOR = 4, REDCOLOR = 5, WHITECOLOR = 6 }; /* Constants related to Color CalibrationState */ /* When STARTCAL is TRUE then calibration is */ /* in progress */ enum { SENSORCAL = 0x01, SENSOROFF = 0x02, RUNNINGCAL = 0x20, STARTCAL = 0x40, RESETCAL = 0x80, }; enum { CAL_POINT_0, CAL_POINT_1, CAL_POINT_2, NO_OF_POINTS }; typedef struct { UWORD CustomZeroOffset; /* Set the offset of the custom sensor */ UWORD ADRaw; UWORD SensorRaw; SWORD SensorValue; UBYTE SensorType; UBYTE SensorMode; UBYTE SensorBoolean; UBYTE DigiPinsDir; /* Direction of the Digital pins 1 is output 0 is input */ UBYTE DigiPinsIn; /* Contains the status of the digital pins */ UBYTE DigiPinsOut; /* Sets the output level of the digital pins */ UBYTE CustomPctFullScale; /* Sets the Pct full scale of the custom sensor */ UBYTE CustomActiveStatus; /* Sets the active or inactive state of the custom sensor */ UBYTE InvalidData; /* Indicates wether data is invalid (1) or valid (0) */ UBYTE Spare1; UBYTE Spare2; UBYTE Spare3; }INPUTSTRUCT; typedef struct { ULONG Calibration[NO_OF_POINTS][NO_OF_COLORS]; UWORD CalLimits[NO_OF_POINTS - 1]; UWORD ADRaw[NO_OF_COLORS]; UWORD SensorRaw[NO_OF_COLORS]; SWORD SensorValue[NO_OF_COLORS]; UBYTE Boolean[NO_OF_COLORS]; UBYTE CalibrationState; UBYTE Free1; UBYTE Free2; UBYTE Free3; }COLORSTRUCT; typedef struct { INPUTSTRUCT Inputs[NO_OF_INPUTS]; COLORSTRUCT Colors[NO_OF_INPUTS]; }IOMAPINPUT; #endif nxt-firmware-1.29.7/src/c_ioctrl.c000066400000000000000000000030151466344546000170010ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_ioctrl.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_ioct $ // // Platform C // #include "stdconst.h" #include "modules.h" #include "c_ioctrl.iom" #include "c_ioctrl.h" #include "d_ioctrl.h" static IOMAPIOCTRL IOMapIOCtrl; static VARSIOCTRL VarsIOCtrl; const HEADER cIOCtrl = { 0x00060001L, "IOCtrl", cIOCtrlInit, cIOCtrlCtrl, cIOCtrlExit, (void *)&IOMapIOCtrl, (void *)&VarsIOCtrl, (UWORD)sizeof(IOMapIOCtrl), (UWORD)sizeof(VarsIOCtrl), 0x0000 //Code size - not used so far }; void cIOCtrlInit(void* pHeader) { dIOCtrlSetPower(0); dIOCtrlInit(); } void cIOCtrlCtrl(void) { switch(IOMapIOCtrl.PowerOn) { case POWERDOWN: { dIOCtrlSetPower((POWERDOWN>>8)); } break; case BOOT: { dIOCtrlSetPower((UBYTE)(BOOT>>8)); dIOCtrlSetPwm((UBYTE)BOOT); } break; default: { /* No need to change the default value */ /* if value is boot or reset it should come */ /* back from reset - setting the value to 0 */ } break; } dIOCtrlTransfer(); } void cIOCtrlExit(void) { dIOCtrlExit(); } nxt-firmware-1.29.7/src/c_ioctrl.h000066400000000000000000000011771466344546000170150ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_ioctrl.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_ioct $ // // Platform C // #ifndef C_IOCTRL #define C_IOCTRL typedef struct { UBYTE Tmp; }VARSIOCTRL; void cIOCtrlInit(void* pHeader); void cIOCtrlCtrl(void); void cIOCtrlExit(void); extern const HEADER cIOCtrl; #endif nxt-firmware-1.29.7/src/c_ioctrl.iom000066400000000000000000000012241466344546000173430ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_ioctrl.iom $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_ioct $ // // Platform C // #ifndef CIOCTRL_IOM #define CIOCTRL_IOM #define pMapIoCtrl ((IOMAPIOCTRL*)(pHeaders[ENTRY_IOCTRL]->pIOMap)) enum { POWERDOWN = 0x5A00, BOOT = 0xA55A }; typedef struct { UWORD PowerOn; }IOMAPIOCTRL; #endif nxt-firmware-1.29.7/src/c_loader.c000066400000000000000000000252671466344546000167700ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 12-03-08 15:28 $ // // Filename $Workfile:: c_loader.c $ // // Version $Revision:: 5 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_load $ // // Platform C // #include "stdconst.h" #include "modules.h" #include "c_loader.iom" #include "c_ioctrl.iom" #include "d_loader.h" #include "c_loader.h" static IOMAPLOADER IOMapLoader; static VARSLOADER VarsLoader; static HEADER **pHeaders; const HEADER cLoader = { 0x00090001L, "Loader", cLoaderInit, cLoaderCtrl, cLoaderExit, (void *)&IOMapLoader, (void *)&VarsLoader, (UWORD)sizeof(IOMapLoader), (UWORD)sizeof(VarsLoader), 0x0000 //Code size - not used so far }; UWORD cLoaderFileRq(UBYTE Cmd, UBYTE *pFileName, UBYTE *pBuffer, ULONG *pLength); UWORD cLoaderGetIoMapInfo(ULONG ModuleId, UBYTE *pIoMap, UWORD *pIoMapSize); UWORD cLoaderFindModule(UBYTE *pBuffer); void cLoaderGetModuleName(UBYTE *pDst, UBYTE *pModule); void cLoaderInit(void* pHeader) { IOMapLoader.pFunc = &cLoaderFileRq; VarsLoader.IoMapHandle = FALSE; pHeaders = pHeader; dLoaderInit(); IOMapLoader.FreeUserFlash = dLoaderReturnFreeUserFlash(); } void cLoaderCtrl(void) { } UWORD cLoaderFileRq(UBYTE Cmd, UBYTE *pFileName, UBYTE *pBuffer, ULONG *pLength) { UWORD ReturnState; ReturnState = SUCCESS; switch(Cmd) { case OPENREAD: { ReturnState = dLoaderOpenRead(pFileName, pLength); if (0x8000 <= ReturnState) { dLoaderCloseHandle(ReturnState); } } break; case OPENREADLINEAR: { ReturnState = dLoaderGetFilePtr(pFileName, pBuffer, pLength); if (0x8000 <= ReturnState) { dLoaderCloseHandle(ReturnState); } } break; case OPENWRITE: { /* This is to create a new file */ ReturnState = dLoaderCreateFileHeader(*pLength, pFileName, (UBYTE) NONLINEAR, SYSTEMFILE); if (0x8000 <= ReturnState) { dLoaderCloseHandle(ReturnState); } else { IOMapLoader.FreeUserFlash = dLoaderReturnFreeUserFlash(); } } break; case OPENWRITELINEAR: { ReturnState = dLoaderCreateFileHeader(*pLength, pFileName, (UBYTE) LINEAR, SYSTEMFILE); if (0x8000 <= ReturnState) { dLoaderCloseHandle(ReturnState); } else { IOMapLoader.FreeUserFlash = dLoaderReturnFreeUserFlash(); } } break; case OPENWRITEDATA: { ReturnState = dLoaderCreateFileHeader(*pLength, pFileName, (UBYTE) NONLINEAR, DATAFILE); if (0x8000 <= ReturnState) { dLoaderCloseHandle(ReturnState); } else { IOMapLoader.FreeUserFlash = dLoaderReturnFreeUserFlash(); } } break; case OPENAPPENDDATA: { ReturnState = dLoaderOpenAppend(pFileName, pLength); if (LOADER_ERR(ReturnState) != SUCCESS) { dLoaderCloseHandle(ReturnState); } } break; case CLOSE: { ReturnState = dLoaderCloseHandle(*pFileName); } break; case CROPDATAFILE: { ReturnState = dLoaderCropDatafile(*pFileName); IOMapLoader.FreeUserFlash = dLoaderReturnFreeUserFlash(); } break; case READ: { ReturnState = dLoaderRead(*pFileName, pBuffer, pLength); } break; case WRITE: { ReturnState = dLoaderWriteData(*pFileName, pBuffer, (UWORD*)pLength); } break; case FINDFIRST: { ULONG DataLength; ReturnState = dLoaderFind(pFileName, pBuffer, pLength, &DataLength, (UBYTE) SEARCHING); if (0x8000 <= ReturnState) { dLoaderCloseHandle(ReturnState); } } break; case FINDNEXT: { UWORD Handle; ULONG DataLength; Handle = *pFileName; ReturnState = dLoaderFindNext(Handle, pBuffer, pLength, &DataLength); } break; case DELETE: { ReturnState = dLoaderDelete(pFileName); IOMapLoader.FreeUserFlash = dLoaderReturnFreeUserFlash(); } break; case DELETEUSERFLASH: { dLoaderDeleteAllFiles(); IOMapLoader.FreeUserFlash = dLoaderReturnFreeUserFlash(); } break; case FINDFIRSTMODULE: { if (FALSE == VarsLoader.IoMapHandle) { VarsLoader.IoMapHandle = TRUE; VarsLoader.ModSearchIndex = 0; dLoaderInsertSearchStr(VarsLoader.ModSearchStr, pFileName, &(VarsLoader.ModSearchType)); ReturnState = cLoaderFindModule(pBuffer); } else { ReturnState = NOMOREHANDLES; } } break; case FINDNEXTMODULE: { ReturnState = cLoaderFindModule(pBuffer); } break; case CLOSEMODHANDLE: { VarsLoader.IoMapHandle = FALSE; ReturnState = SUCCESS; } break; case IOMAPREAD: { UBYTE *pIoMap; ULONG Ptr; UWORD IoMapSize; UBYTE Tmp; pIoMap = NULL; ReturnState = cLoaderGetIoMapInfo((*(ULONG*)(pFileName)),(UBYTE*)(&pIoMap), &IoMapSize); /* Did we have a valid module ID ?*/ if (SUCCESS == LOADER_ERR(ReturnState)) { /* This is the offset */ Ptr = pBuffer[0]; Ptr |= (UWORD)pBuffer[1] << 8; /* is the offset within the limits of the iomap size? */ if ((Ptr + *pLength) <= IoMapSize) { /* Add the offset to the pointer */ pIoMap += Ptr; for (Tmp = 0; Tmp < *pLength; Tmp++) { pBuffer[Tmp + 2] = *pIoMap; pIoMap++; } } else { /* Error - not within the bounderies */ ReturnState = OUTOFBOUNDERY; *pLength = 0; } } else { /* Error - not a valid module id */ *pLength = 0; } } break; case IOMAPWRITE: { UBYTE *pIoMap; ULONG Ptr; UWORD IoMapSize; UWORD Tmp; pIoMap = NULL; ReturnState = cLoaderGetIoMapInfo(*((ULONG*)pFileName), (UBYTE*)&pIoMap, &IoMapSize); if (LOADER_ERR(ReturnState) == SUCCESS) { /* This is the offset */ Ptr = *pBuffer; pBuffer++; Tmp = *pBuffer; Ptr |= Tmp << 8; pBuffer++; if ((Ptr + *pLength) <= IoMapSize) { pIoMap += Ptr; for (Tmp = 0; Tmp < *pLength; Tmp++) { *pIoMap = pBuffer[Tmp]; pIoMap++; } } else { /* Error - not within the bounderies */ ReturnState = OUTOFBOUNDERY; *pLength = 0; } } else { /* Error - not a valid module id */ *pLength = 0; } } break; case RENAMEFILE: { UBYTE FoundName[FILENAME_LENGTH + 1]; /* Check for file exists*/ ReturnState = dLoaderFind(pBuffer, FoundName, pLength, pLength, (UBYTE) SEARCHING); dLoaderCloseHandle(LOADER_HANDLE(ReturnState)); if (FILENOTFOUND == LOADER_ERR(ReturnState)) { ReturnState = dLoaderFind(pFileName, FoundName, pLength, pLength, (UBYTE) SEARCHING); if (ReturnState < 0x8000) { ReturnState = dLoaderCheckFiles((UBYTE) ReturnState); if (ReturnState < 0x8000) { dLoaderRenameFile((UBYTE) ReturnState, pBuffer); } } dLoaderCloseHandle(LOADER_HANDLE(ReturnState)); } else { if (SUCCESS == LOADER_ERR(ReturnState)) { ReturnState |= FILEEXISTS; } } } break; default: { } break; } return (ReturnState); } UWORD cLoaderGetIoMapInfo(ULONG ModuleId, UBYTE *pIoMap, UWORD *pIoMapSize) { UBYTE Tmp; UBYTE Exit; UWORD RtnVal; RtnVal = SUCCESS; Tmp = 0; Exit = FALSE; while((Tmp < 32) && (Exit == FALSE)) { if ((*(pHeaders[Tmp])).ModuleID == ModuleId) { Exit = TRUE; } else { Tmp++; } } /* Did we have a valid module ID ?*/ if (TRUE == Exit) { /* Get the pointer of the module io map */ *((ULONG *)pIoMap) = (ULONG)((*(pHeaders[Tmp])).pIOMap); *pIoMapSize = (*(pHeaders[Tmp])).IOMapSize; } else { RtnVal = MODULENOTFOUND; } /* To avoid a warning - this is optimized away */ *pIoMap = *pIoMap; return(RtnVal); } UWORD cLoaderFindModule(UBYTE *pBuffer) { UBYTE Tmp; UWORD RtnVal; UBYTE ModuleName[FILENAME_SIZE]; RtnVal = MODULENOTFOUND; for (Tmp = VarsLoader.ModSearchIndex; Tmp < 32; Tmp++) { if (pHeaders[Tmp] != 0) { cLoaderGetModuleName(ModuleName, ((*(pHeaders[Tmp])).ModuleName)); if (SUCCESS == dLoaderCheckName(ModuleName, VarsLoader.ModSearchStr, VarsLoader.ModSearchType)) { dLoaderCopyFileName(pBuffer, ModuleName); pBuffer[FILENAME_SIZE] = (UBYTE) ((*(pHeaders[Tmp])).ModuleID); pBuffer[FILENAME_SIZE + 1] = (UBYTE)(((*(pHeaders[Tmp])).ModuleID) >> 8); pBuffer[FILENAME_SIZE + 2] = (UBYTE)(((*(pHeaders[Tmp])).ModuleID) >> 16); pBuffer[FILENAME_SIZE + 3] = (UBYTE)(((*(pHeaders[Tmp])).ModuleID) >> 24); pBuffer[FILENAME_SIZE + 4] = (UBYTE)(((*(pHeaders[Tmp])).ModuleSize)); pBuffer[FILENAME_SIZE + 5] = (UBYTE)(((*(pHeaders[Tmp])).ModuleSize) >> 8); pBuffer[FILENAME_SIZE + 6] = (UBYTE)(((*(pHeaders[Tmp])).ModuleSize) >> 16); pBuffer[FILENAME_SIZE + 7] = (UBYTE)(((*(pHeaders[Tmp])).ModuleSize) >> 24); pBuffer[FILENAME_SIZE + 8] = (UBYTE) ((*(pHeaders[Tmp])).IOMapSize); pBuffer[FILENAME_SIZE + 9] = (UBYTE)(((*(pHeaders[Tmp])).IOMapSize) >> 8); RtnVal = SUCCESS; (VarsLoader.ModSearchIndex) = Tmp + 1; Tmp = 32; } } } return(RtnVal); } void cLoaderGetModuleName(UBYTE *pDst, UBYTE *pModule) { UBYTE Tmp; for(Tmp = 0; Tmp < FILENAME_SIZE; Tmp++) { if (0 != pModule[Tmp]) { pDst[Tmp] = pModule[Tmp]; } else { pDst[Tmp++] = '.'; pDst[Tmp++] = 'm'; pDst[Tmp++] = 'o'; pDst[Tmp++] = 'd'; pDst[Tmp] = '\0'; Tmp = FILENAME_SIZE; } } } void cLoaderExit(void) { } nxt-firmware-1.29.7/src/c_loader.h000066400000000000000000000015371466344546000167670ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_loader.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_load $ // // Platform C // #ifndef C_LOADER #define C_LOADER enum { LOADER_BUSY, TOO_MANY_FILES, NO_MORE_FLASH, LOADER_SUCCESS }; typedef struct { UBYTE ModSearchStr[FILENAME_LENGTH + 1]; UBYTE ModSearchIndex; UBYTE ModSearchType; UBYTE UsbStatus; UBYTE IoMapHandle; }VARSLOADER; void cLoaderInit(void* pHeader); void cLoaderCtrl(void); void cLoaderExit(void); extern const HEADER cLoader; #endif nxt-firmware-1.29.7/src/c_loader.iom000066400000000000000000000045341466344546000173240ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 24-06-09 8:53 $ // // Filename $Workfile:: c_loader.iom $ // // Version $Revision:: 15 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_load $ // // Platform C // #ifndef CLOADER_IOM #define CLOADER_IOM #define pMapLoader ((IOMAPLOADER*)(pHeaders[ENTRY_LOADER]->pIOMap)) //Version numbers are two bytes, MAJOR.MINOR (big-endian) //For example, version 1.5 would be 0x0105 //If these switch to little-endian, be sure to update //definition and usages of VM_OLDEST_COMPATIBLE_VERSION, too! #define FIRMWAREVERSION 0x011D // x.y #define FIRMWAREPATCH 7 // .z, the third number #define PROTOCOLVERSION 0x017C //1.124 enum { OPENREAD = 0x80, OPENWRITE = 0x81, READ = 0x82, WRITE = 0x83, CLOSE = 0x84, DELETE = 0x85, FINDFIRST = 0x86, FINDNEXT = 0x87, VERSIONS = 0x88, OPENWRITELINEAR = 0x89, OPENREADLINEAR = 0x8A, OPENWRITEDATA = 0x8B, OPENAPPENDDATA = 0x8C, CROPDATAFILE = 0x8D, /* New cmd for datalogging */ FINDFIRSTMODULE = 0x90, FINDNEXTMODULE = 0x91, CLOSEMODHANDLE = 0x92, IOMAPREAD = 0x94, IOMAPWRITE = 0x95, BOOTCMD = 0x97, /* external command only */ SETBRICKNAME = 0x98, BTGETADR = 0x9A, DEVICEINFO = 0x9B, DELETEUSERFLASH = 0xA0, POLLCMDLEN = 0xA1, POLLCMD = 0xA2, RENAMEFILE = 0xA3, BTFACTORYRESET = 0xA4 }; typedef UWORD LOADER_STATUS; //Mask out handle byte of Loader status word for error code checks #define LOADER_ERR(StatusWord) ((StatusWord & 0xFF00)) //Byte value of error half of Loader status word #define LOADER_ERR_BYTE(StatusWord) ((UBYTE)((StatusWord & 0xFF00) >> 8)) //Value of handle inside Loader status word #define LOADER_HANDLE(StatusWord) ((UBYTE)(StatusWord)) //Pointer to lower byte of Loader status word #define LOADER_HANDLE_P(StatusWord) ((UBYTE*)(&StatusWord)) typedef struct { UWORD (*pFunc)(UBYTE, UBYTE *, UBYTE *, ULONG *); ULONG FreeUserFlash; }IOMAPLOADER; #endif nxt-firmware-1.29.7/src/c_lowspeed.c000066400000000000000000000217201466344546000173320ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_lowspeed.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_lows $ // // Platform C // #include "stdconst.h" #include "modules.h" #include "c_lowspeed.iom" #include "c_input.iom" #include "c_lowspeed.h" #include "d_lowspeed.h" static IOMAPLOWSPEED IOMapLowSpeed; static VARSLOWSPEED VarsLowSpeed; static HEADER **pHeaders; const UBYTE LOWSPEED_CH_NUMBER[4] = {0x01, 0x02, 0x04, 0x08}; const HEADER cLowSpeed = { 0x000B0001L, "Low Speed", cLowSpeedInit, cLowSpeedCtrl, cLowSpeedExit, (void *)&IOMapLowSpeed, (void *)&VarsLowSpeed, (UWORD)sizeof(IOMapLowSpeed), (UWORD)sizeof(VarsLowSpeed), 0x0000 //Code size - not used so far }; void cLowSpeedInit(void* pHeader) { pHeaders = pHeader; dLowSpeedInit(); IOMapLowSpeed.State = COM_CHANNEL_NONE_ACTIVE; VarsLowSpeed.TimerState = TIMER_STOPPED; } void cLowSpeedCtrl(void) { UBYTE Temp; UBYTE ChannelNumber = 0; if (IOMapLowSpeed.State != 0) { for (ChannelNumber = 0; ChannelNumber < NO_OF_LOWSPEED_COM_CHANNEL; ChannelNumber++) { //Lowspeed com is activated switch (IOMapLowSpeed.ChannelState[ChannelNumber]) { case LOWSPEED_IDLE: { } break; case LOWSPEED_INIT: { if ((pMapInput->Inputs[ChannelNumber].SensorType == LOWSPEED) || (pMapInput->Inputs[ChannelNumber].SensorType == LOWSPEED_9V)) { if (VarsLowSpeed.TimerState == TIMER_STOPPED) { dLowSpeedStartTimer(); VarsLowSpeed.TimerState = TIMER_RUNNING; } IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_LOAD_BUFFER; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_NO_ERROR; VarsLowSpeed.ErrorCount[ChannelNumber] = 0; dLowSpeedInitPins(ChannelNumber); } else { IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_ERROR; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_CH_NOT_READY; } } break; case LOWSPEED_LOAD_BUFFER: { if ((pMapInput->Inputs[ChannelNumber].SensorType == LOWSPEED) || (pMapInput->Inputs[ChannelNumber].SensorType == LOWSPEED_9V)) { VarsLowSpeed.OutputBuf[ChannelNumber].OutPtr = 0; for (VarsLowSpeed.OutputBuf[ChannelNumber].InPtr = 0; VarsLowSpeed.OutputBuf[ChannelNumber].InPtr < IOMapLowSpeed.OutBuf[ChannelNumber].InPtr; VarsLowSpeed.OutputBuf[ChannelNumber].InPtr++) { VarsLowSpeed.OutputBuf[ChannelNumber].Buf[VarsLowSpeed.OutputBuf[ChannelNumber].InPtr] = IOMapLowSpeed.OutBuf[ChannelNumber].Buf[IOMapLowSpeed.OutBuf[ChannelNumber].OutPtr]; IOMapLowSpeed.OutBuf[ChannelNumber].OutPtr++; } if (dLowSpeedSendData(ChannelNumber, &VarsLowSpeed.OutputBuf[ChannelNumber].Buf[0], (VarsLowSpeed.OutputBuf[ChannelNumber].InPtr - VarsLowSpeed.OutputBuf[ChannelNumber].OutPtr))) { if (IOMapLowSpeed.InBuf[ChannelNumber].BytesToRx != 0) { dLowSpeedReceiveData(ChannelNumber, &VarsLowSpeed.InputBuf[ChannelNumber].Buf[0], IOMapLowSpeed.InBuf[ChannelNumber].BytesToRx); VarsLowSpeed.RxTimeCnt[ChannelNumber] = 0; } IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_COMMUNICATING; IOMapLowSpeed.Mode[ChannelNumber] = LOWSPEED_TRANSMITTING; } else { IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_ERROR; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_CH_NOT_READY; } } else { IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_ERROR; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_CH_NOT_READY; } } break; case LOWSPEED_COMMUNICATING: { if ((pMapInput->Inputs[ChannelNumber].SensorType == LOWSPEED) || (pMapInput->Inputs[ChannelNumber].SensorType == LOWSPEED_9V)) { if (IOMapLowSpeed.Mode[ChannelNumber] == LOWSPEED_TRANSMITTING) { Temp = dLowSpeedComTxStatus(ChannelNumber); // Returns 0x00 if not done, 0x01 if success, 0xFF if error if (Temp == LOWSPEED_COMMUNICATION_SUCCESS) { if (IOMapLowSpeed.InBuf[ChannelNumber].BytesToRx != 0) { IOMapLowSpeed.Mode[ChannelNumber] = LOWSPEED_RECEIVING; } else { IOMapLowSpeed.Mode[ChannelNumber] = LOWSPEED_DATA_RECEIVED; IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_DONE; } } if (Temp == LOWSPEED_COMMUNICATION_ERROR) { //ERROR in Communication, No ACK received from SLAVE, retry send data 3 times! VarsLowSpeed.ErrorCount[ChannelNumber]++; if (VarsLowSpeed.ErrorCount[ChannelNumber] > MAX_RETRY_TX_COUNT) { IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_ERROR; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_TX_ERROR; } else { IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_LOAD_BUFFER; } } } if (IOMapLowSpeed.Mode[ChannelNumber] == LOWSPEED_RECEIVING) { VarsLowSpeed.RxTimeCnt[ChannelNumber]++; if (VarsLowSpeed.RxTimeCnt[ChannelNumber] > LOWSPEED_RX_TIMEOUT) { IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_ERROR; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_RX_ERROR; } Temp = dLowSpeedComRxStatus(ChannelNumber); if (Temp == LOWSPEED_COMMUNICATION_SUCCESS) { for (VarsLowSpeed.InputBuf[ChannelNumber].OutPtr = 0; VarsLowSpeed.InputBuf[ChannelNumber].OutPtr < IOMapLowSpeed.InBuf[ChannelNumber].BytesToRx; VarsLowSpeed.InputBuf[ChannelNumber].OutPtr++) { IOMapLowSpeed.InBuf[ChannelNumber].Buf[IOMapLowSpeed.InBuf[ChannelNumber].InPtr] = VarsLowSpeed.InputBuf[ChannelNumber].Buf[VarsLowSpeed.InputBuf[ChannelNumber].OutPtr]; IOMapLowSpeed.InBuf[ChannelNumber].InPtr++; if (IOMapLowSpeed.InBuf[ChannelNumber].InPtr >= SIZE_OF_LSBUF) { IOMapLowSpeed.InBuf[ChannelNumber].InPtr = 0; } VarsLowSpeed.InputBuf[ChannelNumber].Buf[VarsLowSpeed.InputBuf[ChannelNumber].OutPtr] = 0; } IOMapLowSpeed.Mode[ChannelNumber] = LOWSPEED_DATA_RECEIVED; IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_DONE; } if (Temp == LOWSPEED_COMMUNICATION_ERROR) { //There was and error in receiving data from the device for (VarsLowSpeed.InputBuf[ChannelNumber].OutPtr = 0; VarsLowSpeed.InputBuf[ChannelNumber].OutPtr < IOMapLowSpeed.InBuf[ChannelNumber].BytesToRx; VarsLowSpeed.InputBuf[ChannelNumber].OutPtr++) { VarsLowSpeed.InputBuf[ChannelNumber].Buf[VarsLowSpeed.InputBuf[ChannelNumber].OutPtr] = 0; } IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_ERROR; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_RX_ERROR; } } } else { IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_ERROR; IOMapLowSpeed.ErrorType[ChannelNumber] = LOWSPEED_CH_NOT_READY; } } break; case LOWSPEED_ERROR: { IOMapLowSpeed.State = IOMapLowSpeed.State & ~LOWSPEED_CH_NUMBER[ChannelNumber]; if (IOMapLowSpeed.State == 0) { dLowSpeedStopTimer(); VarsLowSpeed.TimerState = TIMER_STOPPED; } } break; case LOWSPEED_DONE: { IOMapLowSpeed.State = IOMapLowSpeed.State & ~LOWSPEED_CH_NUMBER[ChannelNumber]; IOMapLowSpeed.ChannelState[ChannelNumber] = LOWSPEED_IDLE; if (IOMapLowSpeed.State == 0) { dLowSpeedStopTimer(); VarsLowSpeed.TimerState = TIMER_STOPPED; } } break; default: break; } } } } void cLowSpeedExit(void) { dLowSpeedExit(); } nxt-firmware-1.29.7/src/c_lowspeed.h000066400000000000000000000025061466344546000173400ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_lowspeed.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_lows $ // // Platform C // #ifndef C_LOWSPEED #define C_LOWSPEED #define LOWSPEED_RX_TIMEOUT 100 #define LOWSPEED_COMMUNICATION_SUCCESS 0x01 #define LOWSPEED_COMMUNICATION_ERROR 0xFF #define SIZE_OF_LSBUFDATA 16 #define NO_OF_LOWSPEED_COM_CH 4 enum { LOWSPEED_CHANNEL1, LOWSPEED_CHANNEL2, LOWSPEED_CHANNEL3, LOWSPEED_CHANNEL4 }; enum { TIMER_STOPPED, TIMER_RUNNING }; typedef struct { UBYTE Buf[SIZE_OF_LSBUFDATA]; UBYTE InPtr; UBYTE OutPtr; }LSDATA; typedef struct { LSDATA OutputBuf[NO_OF_LOWSPEED_COM_CH]; LSDATA InputBuf[NO_OF_LOWSPEED_COM_CH]; UBYTE RxTimeCnt[NO_OF_LOWSPEED_COM_CH]; UBYTE ErrorCount[NO_OF_LOWSPEED_COM_CH]; UBYTE Tmp; UBYTE TimerState; }VARSLOWSPEED; void cLowSpeedInit(void* pHeader); void cLowSpeedCtrl(void); void cLowSpeedExit(void); extern const HEADER cLowSpeed; #endif nxt-firmware-1.29.7/src/c_lowspeed.iom000066400000000000000000000035461466344546000177020ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_lowspeed.iom $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_lows $ // // Platform C // #ifndef CLOWSPEED_IOM #define CLOWSPEED_IOM #define pMapLowSpeed ((IOMAPLOWSPEED*)(pHeaders[ENTRY_LOWSPEED]->pIOMap)) #define MAX_RETRY_TX_COUNT 3 #define NO_OF_LOWSPEED_COM_CHANNEL 4 #define NO_OF_LSBUF NO_OF_LOWSPEED_COM_CHANNEL #define SIZE_OF_LSBUF 16 //Constants referring to LowSpeedDeviceType enum { ULTRA_SONIC = 2, CUSTOM_LS_DEVICE }; // Constants reffering to State enum { COM_CHANNEL_NONE_ACTIVE = 0x00, COM_CHANNEL_ONE_ACTIVE = 0x01, COM_CHANNEL_TWO_ACTIVE = 0x02, COM_CHANNEL_THREE_ACTIVE = 0x04, COM_CHANNEL_FOUR_ACTIVE = 0x08 }; // Constants reffering to ChannelState enum { LOWSPEED_IDLE, LOWSPEED_INIT, LOWSPEED_LOAD_BUFFER, LOWSPEED_COMMUNICATING, LOWSPEED_ERROR, LOWSPEED_DONE }; // Constants reffering to Mode enum { LOWSPEED_TRANSMITTING = 1, LOWSPEED_RECEIVING, LOWSPEED_DATA_RECEIVED }; // Constants reffering to ErrorType enum { LOWSPEED_NO_ERROR = 0, LOWSPEED_CH_NOT_READY, LOWSPEED_TX_ERROR, LOWSPEED_RX_ERROR }; typedef struct { UBYTE Buf[SIZE_OF_LSBUF]; UBYTE InPtr; UBYTE OutPtr; UBYTE BytesToRx; }LSBUF; typedef struct { LSBUF InBuf[NO_OF_LSBUF]; LSBUF OutBuf[NO_OF_LSBUF]; UBYTE Mode[NO_OF_LSBUF]; UBYTE ChannelState[NO_OF_LSBUF]; UBYTE ErrorType[NO_OF_LSBUF]; UBYTE State; UBYTE Speed; UBYTE Spare1; }IOMAPLOWSPEED; #endif nxt-firmware-1.29.7/src/c_output.c000066400000000000000000000114631466344546000170530ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_output.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_outp $ // // Platform C // #include #include "stdbool.h" #include "stdconst.h" #include "modules.h" #include "c_output.iom" #include "c_output.h" #include "d_output.h" #include "c_display.iom" static IOMAPOUTPUT IOMapOutput; static VARSOUTPUT VarsOutput; const HEADER cOutput = { 0x00020001L, "Output", cOutputInit, cOutputCtrl, cOutputExit, (void *)&IOMapOutput, (void *)&VarsOutput, (UWORD)sizeof(IOMapOutput), (UWORD)sizeof(VarsOutput), 0x0000 //Code size - not used so far }; void cOutputInit(void* pHeader) { UBYTE Tmp; for(Tmp = 0; Tmp < NO_OF_OUTPUTS; Tmp++) { OUTPUT * pOut = &(IOMapOutput.Outputs[Tmp]); pOut->Mode = 0x00; pOut->Speed = 0x00; pOut->ActualSpeed = 0x00; pOut->TachoCnt = 0x00; pOut->RunState = 0x00; pOut->TachoLimit = 0x00; pOut->RegPParameter = DEFAULT_P_GAIN_FACTOR; pOut->RegIParameter = DEFAULT_I_GAIN_FACTOR; pOut->RegDParameter = DEFAULT_D_GAIN_FACTOR; pOut->Options = 0x00; pOut->MaxSpeed = DEFAULT_MAX_SPEED; pOut->MaxAcceleration = DEFAULT_MAX_ACCELERATION; } IOMapOutput.RegulationTime = REGULATION_TIME; IOMapOutput.RegulationOptions = 0; VarsOutput.TimeCnt = 0; dOutputInit(); } void cOutputCtrl(void) { UBYTE Tmp; for(Tmp = 0; Tmp < NO_OF_OUTPUTS; Tmp++) { OUTPUT * pOut = &(IOMapOutput.Outputs[Tmp]); if (pOut->Flags != 0) { if (pOut->Flags & UPDATE_RESET_ROTATION_COUNT) { pOut->Flags &= ~UPDATE_RESET_ROTATION_COUNT; dOutputResetRotationCaptureCount(Tmp); } if (pOut->Flags & UPDATE_RESET_COUNT) { pOut->Flags &= ~UPDATE_RESET_COUNT; dOutputResetTachoLimit(Tmp); } if (pOut->Flags & UPDATE_RESET_BLOCK_COUNT) { pOut->Flags &= ~UPDATE_RESET_BLOCK_COUNT; dOutputResetBlockTachoLimit(Tmp); } if (pOut->Flags & UPDATE_SPEED) { pOut->Flags &= ~UPDATE_SPEED; if (pOut->Mode & MOTORON) { dOutputSetSpeed(Tmp, pOut->RunState, pOut->Speed, pOut->SyncTurnParameter); } } if (pOut->Flags & UPDATE_MODE) { pOut->Flags &= ~UPDATE_MODE; if (pOut->Mode & BRAKE) { // Motor is Braked dOutputSetMode(Tmp, BRAKE); } else { // Motor is floated dOutputSetMode(Tmp, 0x00); } if (pOut->Mode & MOTORON) { if (pOut->Mode & REGULATED) { dOutputEnableRegulation(Tmp, pOut->RegMode); } else { dOutputDisableRegulation(Tmp); } } else { dOutputSetSpeed(Tmp, 0x00, 0x00, 0x00); dOutputDisableRegulation(Tmp); } } if (pOut->Flags & UPDATE_TACHO_LIMIT) { pOut->Flags &= ~UPDATE_TACHO_LIMIT; dOutputSetTachoLimit(Tmp, pOut->TachoLimit); } if (pOut->Flags & UPDATE_PID_VALUES) { pOut->Flags &= ~UPDATE_PID_VALUES; dOutputSetPIDParameters(Tmp, pOut->RegPParameter, pOut->RegIParameter, pOut->RegDParameter); dOutputSetMax(Tmp, pOut->MaxSpeed, pOut->MaxAcceleration); } } } dOutputSetRegulationTime(IOMapOutput.RegulationTime); dOutputSetRegulationOptions(IOMapOutput.RegulationOptions); dOutputCtrl(); cOutputUpdateIomap(); } void cOutputUpdateIomap(void) { UBYTE TempCurrentMotorSpeed[NO_OF_OUTPUTS]; UBYTE TempRunState[NO_OF_OUTPUTS]; UBYTE TempMotorOverloaded[NO_OF_OUTPUTS]; SLONG TempTachoCount[NO_OF_OUTPUTS]; SLONG TempBlockTachoCount[NO_OF_OUTPUTS]; SLONG TempRotationCount[NO_OF_OUTPUTS]; UBYTE Tmp; dOutputGetMotorParameters(TempCurrentMotorSpeed, TempTachoCount, TempBlockTachoCount, TempRunState, TempMotorOverloaded,TempRotationCount); for(Tmp = 0; Tmp < NO_OF_OUTPUTS; Tmp++) { OUTPUT * pOut = &(IOMapOutput.Outputs[Tmp]); pOut->ActualSpeed = TempCurrentMotorSpeed[Tmp]; pOut->TachoCnt = TempTachoCount[Tmp]; pOut->BlockTachoCount = TempBlockTachoCount[Tmp]; pOut->RotationCount = TempRotationCount[Tmp]; pOut->Overloaded = TempMotorOverloaded[Tmp]; if (!(pOut->Flags & PENDING_UPDATES)) { pOut->RunState = TempRunState[Tmp]; } } } void cOutputExit(void) { dOutputExit(); } nxt-firmware-1.29.7/src/c_output.h000066400000000000000000000012701466344546000170530ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_output.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_outp $ // // Platform C // #ifndef C_OUTPUT #define C_OUTPUT typedef struct { UBYTE TimeCnt; UBYTE Tmp; }VARSOUTPUT; void cOutputInit(void* pHeader); void cOutputCtrl(void); void cOutputExit(void); void cOutputUpdateIomap(void); extern const HEADER cOutput; #endif nxt-firmware-1.29.7/src/c_output.iom000066400000000000000000000071011466344546000174070ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_output.iom $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_outp $ // // Platform C // #ifndef COUTPUT_IOM #define COUTPUT_IOM #define NO_OF_OUTPUTS 3 #define pMapOutPut ((IOMAPOUTPUT*)(pHeaders[ENTRY_OUTPUT]->pIOMap)) // Constants reffering to mode enum { MOTORON = 0x01, BRAKE = 0x02, REGULATED = 0x04, REG_METHOD = 0xF0 /* Regulation methods - to be designed! */ }; // Constants related to Flags enum { UPDATE_MODE = 0x01, UPDATE_SPEED = 0x02, UPDATE_TACHO_LIMIT = 0x04, UPDATE_RESET_COUNT = 0x08, UPDATE_PID_VALUES = 0x10, UPDATE_RESET_BLOCK_COUNT = 0x20, UPDATE_RESET_ROTATION_COUNT = 0x40, PENDING_UPDATES = 0x80 }; // Constant related to RunState #define MOTOR_RUN_STATE_IDLE 0x00 #define MOTOR_RUN_STATE_RAMPUP 0x10 #define MOTOR_RUN_STATE_RUNNING 0x20 #define MOTOR_RUN_STATE_RAMPDOWN 0x40 // Constant related to RegMode enum { REGULATION_MODE_IDLE = 0, REGULATION_MODE_MOTOR_SPEED = 1, REGULATION_MODE_MOTOR_SYNC = 2, REGULATION_MODE_MOTOR_POS = 4, }; typedef struct { SLONG TachoCnt; /* R - Holds current number of counts, since last reset, updated every 1 mS */ SLONG BlockTachoCount; /* R - Holds current number of counts for the current output block */ SLONG RotationCount; /* R - Holds current number of counts for the rotation counter to the output */ ULONG TachoLimit; /* RW - Holds number of counts to travel, 0 => Run forever */ SWORD MotorRPM; /* !! Is not updated, will be removed later !! */ UBYTE Flags; /* RW - Holds flags for which data should be updated */ UBYTE Mode; /* RW - Holds motor mode: Run, Break, regulated, ... */ SBYTE Speed; /* RW - Holds the wanted speed */ SBYTE ActualSpeed; /* R - Holds the current motor speed */ UBYTE RegPParameter; /* RW - Holds the P-constant use din the regulation, Is set to a default value at init => Setting this value is optional for the user */ UBYTE RegIParameter; /* RW - Holds the I-constant use din the regulation, Is set to a default value at init => Setting this value is optional for the user */ UBYTE RegDParameter; /* RW - Holds the D-constant use din the regulation, Is set to a default value at init => Setting this value is optional for the user */ UBYTE RunState; /* RW - Holds the current RunState in the output module */ UBYTE RegMode; /* RW - Tells which regulation mode should be used */ UBYTE Overloaded; /* R - True if the motor has been overloaded within speed control regulation */ SBYTE SyncTurnParameter; /* RW - Holds the turning parameter need within MoveBlock */ UBYTE Options; SBYTE MaxSpeed; /* RW - Maximum speed for absolute regulation, or 0 for no limit */ SBYTE MaxAcceleration; /* RW - Maximum acceleration for absolute regulation, or 0 for no limit */ }OUTPUT; typedef struct { OUTPUT Outputs[NO_OF_OUTPUTS]; UBYTE RegulationTime; /* RW - Interval between regulation computations */ UBYTE RegulationOptions; /* RW - Options for regulation, see REGOPTION_* */ }IOMAPOUTPUT; #endif nxt-firmware-1.29.7/src/c_sound.c000066400000000000000000000202401466344546000166340ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_sound.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_soun $ // // Platform C // #include #include #include "stdconst.h" #include "modules.h" #include "c_sound.iom" #include "c_loader.iom" #include "c_sound.h" #include "d_sound.h" static IOMAPSOUND IOMapSound; static VARSSOUND VarsSound; static HEADER **pHeaders; const HEADER cSound = { 0x00080001L, "Sound", cSoundInit, cSoundCtrl, cSoundExit, (void *)&IOMapSound, (void *)&VarsSound, (UWORD)sizeof(IOMapSound), (UWORD)sizeof(VarsSound), 0x0000 //Code size - not used so far }; UWORD cSoundFile(UBYTE Cmd,UBYTE *pFile,UBYTE *pData,ULONG *pLng) { return (pMapLoader->pFunc(Cmd,pFile,pData,pLng)); } void cSoundInit(void* pHeader) { pHeaders = pHeader; IOMapSound.Flags &= ~SOUND_UPDATE; IOMapSound.Flags &= ~SOUND_RUNNING; IOMapSound.State = SOUND_IDLE; IOMapSound.Mode = SOUND_ONCE; IOMapSound.Volume = SOUNDVOLUMESTEPS; IOMapSound.SampleRate = 0; IOMapSound.SoundFilename[0] = 0; VarsSound.BufferIn = 0; VarsSound.BufferOut = 0; dSoundInit(); } void cSoundCtrl(void) { static UWORD FileFormat; static UBYTE SoundFilename[FILENAME_LENGTH + 1]; UWORD Handle; ULONG Length; UBYTE Header[FILEHEADER_LENGTH]; UBYTE In,Out,Tmp; In = VarsSound.BufferIn; Out = VarsSound.BufferOut; if ((IOMapSound.Flags & SOUND_UPDATE)) { // Check if valid update if (!(SOUND_TONE & IOMapSound.Mode)) { Handle = pMapLoader->pFunc(FINDFIRST,IOMapSound.SoundFilename,SoundFilename,&Length); if (!(Handle & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&Handle,NULL,NULL); } else { IOMapSound.Flags &= ~SOUND_UPDATE; } } if ((IOMapSound.Flags & SOUND_UPDATE)) { // Check for open file if (!(VarsSound.File & 0x8000)) { cSoundFile(CLOSE,(UBYTE*)&VarsSound.File,NULL,NULL); VarsSound.File = 0x8000; } IOMapSound.Flags &= ~SOUND_UPDATE; if ((SOUND_TONE & IOMapSound.Mode)) { dSoundFreq(IOMapSound.Freq,IOMapSound.Duration,IOMapSound.Volume); IOMapSound.State = SOUND_FREQ; } else { if (IOMapSound.Flags & SOUND_RUNNING) { dSoundStop(); IOMapSound.Flags &= ~SOUND_RUNNING; } VarsSound.File = pMapLoader->pFunc(OPENREAD,SoundFilename,NULL,&Length); if (!(VarsSound.File & 0x8000)) { Length = FILEHEADER_LENGTH; pMapLoader->pFunc(READ,(UBYTE*)&VarsSound.File,Header,&Length); if (Length == FILEHEADER_LENGTH) { FileFormat = ((UWORD)Header[0] << 8) + (UWORD)Header[1]; if (FILEFORMAT_SOUND == (FileFormat & 0xFF00)) { if (IOMapSound.SampleRate) { VarsSound.SampleRate = IOMapSound.SampleRate; IOMapSound.SampleRate = 0; } else { VarsSound.SampleRate = ((UWORD)Header[4] << 8) + (UWORD)Header[5]; } dSoundVolume(IOMapSound.Volume); Length = SOUNDBUFFERSIZE; pMapLoader->pFunc(READ,(UBYTE*)&VarsSound.File,VarsSound.Buffer[In],&Length); VarsSound.Length[In] = (UWORD)Length; In++; if (In >= SOUNDBUFFERS) { In = 0; } IOMapSound.State = SOUND_BUSY; } else { if (FILEFORMAT_MELODY == FileFormat) { Length = SOUNDBUFFERSIZE; pMapLoader->pFunc(READ,(UBYTE*)&VarsSound.File,VarsSound.Buffer[In],&Length); VarsSound.Length[In] = (UWORD)Length; In++; if (In >= SOUNDBUFFERS) { In = 0; } IOMapSound.State = SOUND_BUSY; } else { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsSound.File,NULL,NULL); } } } } } } } switch (IOMapSound.State) { case SOUND_BUSY : { IOMapSound.Flags |= SOUND_RUNNING; if (In != Out) { if ((FILEFORMAT_SOUND == FileFormat) || (FILEFORMAT_SOUND_COMPRESSED == FileFormat)) { if (dSoundStart(VarsSound.Buffer[Out],VarsSound.Length[Out],VarsSound.SampleRate,(UBYTE)(FileFormat & 0x00FF)) == TRUE) { Out++; if (Out >= SOUNDBUFFERS) { Out = 0; } } } else { if (dSoundTone(VarsSound.Buffer[Out],VarsSound.Length[Out],IOMapSound.Volume) == TRUE) { Out++; if (Out >= SOUNDBUFFERS) { Out = 0; } } } } Tmp = In; Tmp++; if (Tmp >= SOUNDBUFFERS) { Tmp = 0; } if (Tmp != Out) { Tmp++; if (Tmp >= SOUNDBUFFERS) { Tmp = 0; } if (Tmp != Out) { Length = SOUNDBUFFERSIZE; Handle = cSoundFile(READ,(UBYTE*)&VarsSound.File,VarsSound.Buffer[In],&Length); if ((Handle & 0x8000)) { Length = 0L; } VarsSound.Length[In] = (UWORD)Length; if (VarsSound.Length[In] == 0) { if (SOUND_LOOP == IOMapSound.Mode) { if (!(IOMapSound.Flags & SOUND_UPDATE)) { cSoundFile(CLOSE,(UBYTE*)&VarsSound.File,NULL,NULL); VarsSound.File = cSoundFile(OPENREAD,SoundFilename,NULL,&Length); Length = FILEHEADER_LENGTH; cSoundFile(READ,(UBYTE*)&VarsSound.File,Header,&Length); Length = SOUNDBUFFERSIZE; cSoundFile(READ,(UBYTE*)&VarsSound.File,VarsSound.Buffer[In],&Length); VarsSound.Length[In] = (UWORD)Length; } } } if (VarsSound.Length[In] != 0) { In++; if (In >= SOUNDBUFFERS) { In = 0; } } if (VarsSound.Length[Out] == 0) { if (!(VarsSound.File & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsSound.File,NULL,NULL); VarsSound.File = 0x8000; } IOMapSound.Flags &= ~SOUND_RUNNING; IOMapSound.State = SOUND_IDLE; } } } } break; case SOUND_FREQ : { IOMapSound.Flags |= SOUND_RUNNING; if (dSoundReady() == TRUE) { if (SOUND_LOOP & IOMapSound.Mode) { dSoundFreq(IOMapSound.Freq,IOMapSound.Duration,IOMapSound.Volume); } else { IOMapSound.Flags &= ~SOUND_RUNNING; IOMapSound.State = SOUND_IDLE; } } } break; case SOUND_STOP : { dSoundStop(); if (!(VarsSound.File & 0x8000)) { pMapLoader->pFunc(CLOSE,(UBYTE*)&VarsSound.File,NULL,NULL); VarsSound.File = 0x8000; } IOMapSound.Flags &= ~SOUND_RUNNING; IOMapSound.State = SOUND_IDLE; Out = In; } break; } VarsSound.BufferIn = In; VarsSound.BufferOut = Out; } void cSoundExit(void) { dSoundExit(); } nxt-firmware-1.29.7/src/c_sound.h000066400000000000000000000020431466344546000166420ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_sound.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_soun $ // // Platform C // #ifndef C_SOUND #define C_SOUND #define SOUNDBUFFERSIZE 64 // Flash Sector size ? #define SOUNDBUFFERS 3 // Min 3 - max 255 typedef struct { UWORD Length[SOUNDBUFFERS]; UWORD File; UWORD SampleRate; UBYTE Buffer[SOUNDBUFFERS][SOUNDBUFFERSIZE]; UBYTE BufferIn; UBYTE BufferOut; UBYTE BufferTmp; }VARSSOUND; void cSoundInit(void* pHeaders); void cSoundCtrl(void); void cSoundExit(void); extern const HEADER cSound; #endif nxt-firmware-1.29.7/src/c_sound.iom000066400000000000000000000076461466344546000172150ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: c_sound.iom $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_soun $ // // Platform C // #ifndef CSOUND_IOM #define CSOUND_IOM #define pMapSound ((IOMAPSOUND*)(pHeaders[ENTRY_SOUND]->pIOMap)) /* HOW TO Start a sound file strcpy((char*)pMapSound->SoundFilename,"xxxxxxx.rso"); pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_ONCE; pMapSound->Flags |= SOUND_UPDATE; Start and loop a sound file strcpy((char*)pMapSound->SoundFilename,"xxxxxxx.rso"); pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_LOOP; pMapSound->Flags |= SOUND_UPDATE; Start a tone pMapSound->Freq = 440; pMapSound->Duration = 1000; pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_TONE; pMapSound->Flags |= SOUND_UPDATE; Start and loop a tone pMapSound->Freq = 440; pMapSound->Duration = 1000; pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_TONE | SOUND_LOOP; pMapSound->Flags |= SOUND_UPDATE; Test for sound finished if (!(pMapSound->Flags & (SOUND_RUNNING | SOUND_UPDATE))) { // FINISHED } Abort sound or tone pMapSound->State = SOUND_STOP; **** Start always abort running sound or tone **** */ // Constants related to Flags enum { SOUND_UPDATE = 0x01, // W - Make changes take effect SOUND_RUNNING = 0x02 // R - Processing tone or file }; // Constants related to State enum { SOUND_IDLE = 0x00, // R - Idle, ready for start sound (SOUND_UPDATE) SOUND_BUSY = 0x02, // R - Processing file of sound/melody data SOUND_FREQ = 0x03, // R - Processing play tone request SOUND_STOP = 0x04 // W - Stop sound imedately and close hardware }; // Constants related to Mode enum { SOUND_ONCE = 0x00, // W - Only play file once SOUND_LOOP = 0x01, // W - Play file until writing "SOUND_STOP" into "State" or new "update" SOUND_TONE = 0x02 // W - Play tone specified in Freq for Duration ms }; typedef struct { UWORD Freq; // RW - Tone frequency [Hz] UWORD Duration; // RW - Tone duration [mS] UWORD SampleRate; // RW - Sound file sample rate [2000..16000] UBYTE SoundFilename[FILENAME_LENGTH + 1]; // RW - Sound/melody filename UBYTE Flags; // RW - Play flag - descripted above UBYTE State; // RW - Play state - descriped above UBYTE Mode; // RW - Play mode - descriped above UBYTE Volume; // RW - Sound/melody volume [0..4] 0 = off }IOMAPSOUND; #endif nxt-firmware-1.29.7/src/c_ui.c000066400000000000000000001521651466344546000161350ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 10-06-08 9:26 $ // // Filename $Workfile:: c_ui.c $ // // Version $Revision:: 7 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_ui.c $ // // Platform C // #include "stdio.h" #include "string.h" #include "ctype.h" #include "stdconst.h" #include "modules.h" #include "c_ui.iom" #include "c_ui.h" #include "m_sched.h" #include "c_display.iom" #include "c_loader.iom" #include "c_button.iom" #include "c_sound.iom" #include "c_input.iom" #include "c_output.iom" #include "c_ioctrl.iom" #include "c_cmd.iom" #include "c_comm.iom" #include "c_lowspeed.iom" static IOMAPUI IOMapUi; static VARSUI VarsUi; static HEADER **pHeaders; const HEADER cUi = { 0x000C0001L, "Ui", cUiInit, cUiCtrl, cUiExit, (void *)&IOMapUi, (void *)&VarsUi, (UWORD)sizeof(IOMapUi), (UWORD)sizeof(VarsUi), 0x0000 // Code size - not used so far }; // ****** GENERAL GRAPHIC RESOURCES ****************************************** #include "Display.h" // Bitmap for frame used in view and datalog #include "LowBattery.h" // Bitmap showed when low battery occures #include "Font.h" // Font used for all text #include "Step.h" // Bitmap used in On Brick Programming #include "Cursor.h" // Bitmap for cursor #include "Running.h" // Icon collection used for "running" symbol #include "Port.h" // Font used for naming sensor ports in datalog/bluetooth #include "Ok.h" // Bitmap for OK buttom in get user string #include "Wait.h" // Bitmap for feedback #include "Fail.h" // Bitmap for feedback #include "Info.h" // Bitmap for feedback #include "Icons.h" // Icon collection used for menues // ****** INTRO ANIMATION RESOURCES ****************************************** #include "RCXintro_1.h" // Bitmap for picture 1 in the intro animation #include "RCXintro_2.h" // Bitmap for picture 2 in the intro animation #include "RCXintro_3.h" // Bitmap for picture 3 in the intro animation #include "RCXintro_4.h" // Bitmap for picture 4 in the intro animation #include "RCXintro_5.h" // Bitmap for picture 5 in the intro animation #include "RCXintro_6.h" // Bitmap for picture 6 in the intro animation #include "RCXintro_7.h" // Bitmap for picture 7 in the intro animation #include "RCXintro_8.h" // Bitmap for picture 8 in the intro animation #include "RCXintro_9.h" // Bitmap for picture 9 in the intro animation #include "RCXintro_10.h" // Bitmap for picture 10 in the intro animation #include "RCXintro_11.h" // Bitmap for picture 11 in the intro animation #include "RCXintro_12.h" // Bitmap for picture 12 in the intro animation #include "RCXintro_13.h" // Bitmap for picture 13 in the intro animation #include "RCXintro_14.h" // Bitmap for picture 14 in the intro animation #include "RCXintro_15.h" // Bitmap for picture 15 in the intro animation #include "RCXintro_16.h" // Bitmap for picture 16 in the intro animation const BMPMAP *Intro[NO_OF_INTROBITMAPS] = // Picture sequence for the intro animation { (BMPMAP*) &RCXintro_1, (BMPMAP*) &RCXintro_2, (BMPMAP*) &RCXintro_3, (BMPMAP*) &RCXintro_4, (BMPMAP*) &RCXintro_5, (BMPMAP*) &RCXintro_6, (BMPMAP*) &RCXintro_7, (BMPMAP*) &RCXintro_8, (BMPMAP*) &RCXintro_9, (BMPMAP*) &RCXintro_10, (BMPMAP*) &RCXintro_11, (BMPMAP*) &RCXintro_12, (BMPMAP*) &RCXintro_13, (BMPMAP*) &RCXintro_14, (BMPMAP*) &RCXintro_15, (BMPMAP*) &RCXintro_16 }; // ****** STATUS LINE GRAPHIC RESOURCES ************************************** #include "Status.h" // Status icon collection file enum STATUS_NO // Index in status icon collection file { STATUS_NO_NOT_USED, STATUS_NO_RUNNING_0, STATUS_NO_RUNNING_1, STATUS_NO_RUNNING_2, STATUS_NO_RUNNING_3, STATUS_NO_RUNNING_4, STATUS_NO_RUNNING_5, STATUS_NO_RUNNING_6, STATUS_NO_RUNNING_7, STATUS_NO_RUNNING_8, STATUS_NO_RUNNING_9, STATUS_NO_RUNNING_10, STATUS_NO_RUNNING_11, STATUS_NO_BATTERY_0, STATUS_NO_BATTERY_1, STATUS_NO_BATTERY_2, STATUS_NO_BATTERY_3, STATUS_NO_BATTERY_4, STATUS_NO_BATTERY_5, STATUS_NO_RECHARGEABLE_0, STATUS_NO_RECHARGEABLE_1, STATUS_NO_RECHARGEABLE_2, STATUS_NO_RECHARGEABLE_3, STATUS_NO_RECHARGEABLE_4, STATUS_NO_RECHARGEABLE_5, STATUS_NO_BLUETOOTH_0, STATUS_NO_BLUETOOTH_1, STATUS_NO_BLUETOOTH_2, STATUS_NO_BLUETOOTH_3, STATUS_NO_BLUETOOTH_4, STATUS_NO_BLUETOOTH_5, STATUS_NO_USB_0, STATUS_NO_USB_1, STATUS_NO_USB_2, STATUS_NO_USB_3, STATUS_NO_USB_4, STATUS_NO_USB_5 }; // ****** BT DEVICE GRAPHIC RESOURCES **************************************** #include "Devices.h" // Icon collection used for Blue tooth devices // ****** BT CONNECTIONS GRAPHIC RESOURCES *********************************** #include "Connections.h" // Icon collection used for Blue tooth connections // ****** FREE TEXT GRAPHIC RESOURCES **************************************** #include "Ui_txt.h" // Text strings that isn't defined in menu files enum // String index in text string file { TXT_GENERAL_EMPTY, // BlueTooth connect TXT_FB_BT_CONNECTING_WAIT, // "Connecting" TXT_FB_BT_CONNECT_BUSY_FAIL, // "Line is busy" TXT_FB_BT_CONNECTING_FAIL, // "Failed!" // BlueTooth send file TXT_FB_BT_SENDING_NO_CONN_FAIL, // "Connection?" TXT_FB_BT_SENDING_WAIT, // "Sending file" TXT_FB_BT_SENDING_FAIL, // "Failed!" // BlueTooth on/off TXT_FB_BT_TURNING_ON_WAIT, // "Turning on" TXT_FB_BT_TURNING_ON_FAIL, // "Failed!" TXT_FB_BT_TURNING_OFF_WAIT, // "Turning off" TXT_FB_BT_TURNING_OFF_FAIL, // "Failed!" // BlueTooth seach TXT_FB_BT_SEARCHING_WAIT, // "Searching" TXT_FB_BT_SEARCH_ABORTED_INFO, // "Aborted!" TXT_FB_BT_SEARCHING_FAIL, // "Failed!" // BlueTooth device list TXT_FB_BT_REMOVE_FAIL, // "Failed!" // BlueTooth connection list TXT_FB_BT_DISCONNECT_FAIL, // "Failed!" // On Brick Programming TXT_FB_OBP_MEMORY_FULL_FAIL, // "Memory full!" TXT_FB_OBP_FILE_SAVED_INFO, // "File saved" TXT_FB_OBP_FILE_EXIST_FAIL, // "File exist" TXT_FB_OBP_OVERWRITE_FAIL, // "overwrite!" // Datalogging TXT_FB_DL_FILE_SAVED_INFO, // "File saved" TXT_FB_DL_FILE_EXIST_FAIL, // "File exist" TXT_FB_DL_OVERWRITE_FAIL, // "overwrite!" // File delete TXT_FB_FD_FILE_DELETED_INFO, // "File deleted" // Files delete TXT_FB_FD_FILES_INFO, // "Files" TXT_FB_FD_DELETED_INFO, // "deleted" // File run TXT_FILERUN_RUNNING, // "Running" TXT_FILERUN_ABORTED, // "Aborted!" TXT_FILERUN_ENDED, // "Ended" TXT_FILERUN_FILE_ERROR, // "File error!" // Files delete TXT_FILESDELETE_DELETING_ALL, // "Deleting all" TXT_FILESDELETE_S_FILES, // "%s files!" // Datalogging TXT_DATALOGGING_PRESS_EXIT_TO, // "Press exit to" TXT_DATALOGGING_STOP_DATALOGGING, // "stop datalogging" TXT_DATALOGGING_PORT_OCCUPIED, // "Port occupied!" TXT_DATALOGGING_RATE, // "H:MM:SS:00 TXT_DATALOGGING_TIME, // "HH:MM:SS" // File types TXT_FILETYPE_SOUND, // "Sound" TXT_FILETYPE_LMS, // "Software" TXT_FILETYPE_NXT, // "NXT" TXT_FILETYPE_TRY_ME, // "Try me" TXT_FILETYPE_DATA, // "Datalog" // Get user string TXT_GETUSERSTRING_PIN, // "Pin:" TXT_GETUSERSTRING_FILENAME, // "Filename:" // On Brick Programming TXT_ONBRICKPROGRAMMING_PLEASE_USE_PORT, // "Please use port:" TXT_ONBRICKPROGRAMMING_1_TOUCH_SENSOR, // "1 - Touch sensor" TXT_ONBRICKPROGRAMMING_2_SOUND_SENSOR, // "2 - Sound sensor" TXT_ONBRICKPROGRAMMING_3_LIGHT_SENSOR, // "3 - Light sensor" TXT_ONBRICKPROGRAMMING_4_ULTRA_SONIC, // "4 - Ultra sonic" TXT_ONBRICKPROGRAMMING_BC_LR_MOTORS, // "B/C - L/R motors" // View TXT_VIEW_SELECT, // "Select" // BlueTooth device list TXT_BTDEVICELIST_SELECT, // "Select" // BlueTooth connection list TXT_BTCONNECTLIST_SELECT, // "Select" // Bluetooth list errors TXT_FB_BT_ERROR_LR_COULD_NOT_SAVE_1, // BT save data error! TXT_FB_BT_ERROR_LR_COULD_NOT_SAVE_2, // TXT_FB_BT_ERROR_LR_STORE_IS_FULL_1, // BT store is full error! TXT_FB_BT_ERROR_LR_STORE_IS_FULL_2, // TXT_FB_BT_ERROR_LR_UNKOWN_ADDR_1, // BT unknown addr. error! TXT_FB_BT_ERROR_LR_UNKOWN_ADDR_2, // // Datalog errors TXT_FB_DL_ERROR_MEMORY_FULL_1, // Memory is full! TXT_FB_DL_ERROR_MEMORY_FULL_2, // // Power of time TXT_POWEROFFTIME_NEVER // "Never" }; // ****** FILE TYPE GRAPHIC RESOURCES **************************************** #define ALLFILES 0x1A // Icon collection offset enum // File type id's { FILETYPE_ALL, // 0 = All FILETYPE_SOUND, // 1 = Sound FILETYPE_LMS, // 2 = LMS FILETYPE_NXT, // 3 = NXT FILETYPE_TRYME, // 4 = Try me FILETYPE_DATALOG, // 5 = Datalog FILETYPES }; const UBYTE TXT_FILE_EXT[FILETYPES][4] = { "*", // 0 = All TXT_SOUND_EXT, // 1 = Sound TXT_LMS_EXT, // 2 = LMS TXT_NXT_EXT, // 3 = NXT TXT_TRYME_EXT, // 4 = Try me TXT_DATA_EXT // 5 = Datalog }; const UBYTE TXT_FILETYPE[FILETYPES] = { 0, // NA TXT_FILETYPE_SOUND, // 1 = Sound TXT_FILETYPE_LMS, // 2 = LMS TXT_FILETYPE_NXT, // 3 = NXT TXT_FILETYPE_TRY_ME,// 4 = Try me TXT_FILETYPE_DATA // 5 = Datalog }; // ****** POWER OFF DEFINITIONS ********************************************** #define POWER_OFF_TIME_STEPS 6 #define POWER_OFF_TIME_DEFAULT 3 const UBYTE PowerOffTimeSteps[POWER_OFF_TIME_STEPS] = { 0,2,5,10,30,60 }; // [min] // ****** BATTERY DEFINITIONS ************************************************ #define BATTERYLIMITS 4 // [Cnt] #define BATTERYLIMITHYST 100 // [mV] #define RECHARGEABLELIMITHYST 50 // [mV] const UWORD BatteryLimits[BATTERYLIMITS] = { 6100,6500,7000,7500 // [mV] }; const UWORD RechargeableLimits[BATTERYLIMITS] = { 7100,7200,7300,7500 // [mV] }; //******* UI MENU FILE HANDLER ************************************************************************* #include "Mainmenu.h" #include "Submenu01.h" #include "Submenu02.h" #include "Submenu03.h" #include "Submenu04.h" #include "Submenu05.h" #include "Submenu06.h" #include "Submenu07.h" const UBYTE *MenuPointers[] = { (UBYTE*)Mainmenu, (UBYTE*)Submenu01, (UBYTE*)Submenu02, (UBYTE*)Submenu03, (UBYTE*)Submenu04, (UBYTE*)Submenu05, (UBYTE*)Submenu06, (UBYTE*)Submenu07 }; UBYTE* cUiGetMenuPointer(UBYTE FileNo) { return ((UBYTE*)MenuPointers[FileNo]); } //****************************************************************************************************** UBYTE* cUiGetString(UBYTE No) // Get string in text string file { UBYTE *Result = NULL; if (No) { if (No <= sizeof(Ui) / sizeof(Ui[0])) { Result = (UBYTE *) Ui[No - 1]; } } return (Result); } UBYTE cUiReadButtons(void) // Read buttons { UBYTE Result = BUTTON_NONE; if (!(IOMapUi.Flags & UI_DISABLE_LEFT_RIGHT_ENTER)) { if ((pMapButton->State[BTN3] & PRESSED_STATE)) { Result = BUTTON_LEFT; } if ((pMapButton->State[BTN2] & PRESSED_STATE)) { Result = BUTTON_RIGHT; } if ((pMapButton->State[BTN4] & PRESSED_STATE)) { Result = BUTTON_ENTER; } } if (!(IOMapUi.Flags & UI_DISABLE_EXIT)) { if ((pMapButton->State[BTN1] & PRESSED_STATE)) { Result = BUTTON_EXIT; } } if (Result == BUTTON_NONE) { // All buttons released VarsUi.ButtonOld = BUTTON_NONE; VarsUi.ButtonTime = BUTTON_DELAY_TIME; } else { // Some button pressed if (VarsUi.ButtonOld == BUTTON_NONE) { // Just pressed VarsUi.ButtonOld = Result; VarsUi.ButtonTimer = 0; } else { // Still pressed Result = BUTTON_NONE; if (VarsUi.ButtonTimer >= VarsUi.ButtonTime) { VarsUi.ButtonTimer = 0; VarsUi.ButtonTime = BUTTON_REPEAT_TIME; if ((VarsUi.ButtonOld == BUTTON_LEFT) || (VarsUi.ButtonOld == BUTTON_RIGHT)) { // If arrow repeat Result = VarsUi.ButtonOld; } } } } if (VarsUi.ButtonOld == BUTTON_NONE) { // If no key - check interface Result = IOMapUi.Button; IOMapUi.Button = BUTTON_NONE; } if (Result != BUTTON_NONE) { // If key - play key sound file sprintf((char*)pMapSound->SoundFilename,"%s.%s",(char*)UI_KEYCLICK_SOUND,(char*)TXT_FILE_EXT[FILETYPE_SOUND]); pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_ONCE; pMapSound->Flags |= SOUND_UPDATE; // Reset power down timer IOMapUi.Flags |= UI_RESET_SLEEP_TIMER; } return (Result); } void cUiListLeft(UBYTE Limit,UBYTE *Center) { UBYTE Tmp; Tmp = *Center; if (Tmp > 1) { Tmp--; } else { if (Limit > 2) { Tmp = Limit; } } *Center = Tmp; } void cUiListRight(UBYTE Limit,UBYTE *Center) { UBYTE Tmp; Tmp = *Center; if (Tmp < Limit) { Tmp++; } else { if (Limit > 2) { Tmp = 1; } } *Center = Tmp; } void cUiListCalc(UBYTE Limit,UBYTE *Center,UBYTE *Left,UBYTE *Right) { switch (Limit) { case 1 : { *Left = 0; *Right = 0; } break; case 2 : { if ((*Center) == 1) { *Left = 0; *Right = 2; } else { *Left = 1; *Right = 0; } } break; default : { *Left = *Center - 1; if ((*Left) < 1) { *Left = Limit; } *Right = *Center + 1; if ((*Right) > Limit) { *Right = 1; } } break; } } UBYTE cUiMenuSearchSensorIcon(UBYTE Sensor) { UBYTE Result = 0; MENUITEM *MenuItem; UBYTE Index; for (Index = 0;(Index < IOMapUi.pMenu->Items) && (Result == 0);Index++) { MenuItem = &IOMapUi.pMenu->Data[Index]; if (MenuItem->FunctionParameter == Sensor) { Result = MenuItem->IconImageNo; } } return (Result); } ULONG cUiMenuGetId(MENUITEM *pMenuItem) { ULONG MenuId; MenuId = (ULONG)pMenuItem->ItemId01; MenuId |= (ULONG)pMenuItem->ItemId23 << 8; MenuId |= (ULONG)pMenuItem->ItemId45 << 16; MenuId |= (ULONG)pMenuItem->ItemId67 << 24; return (MenuId); } ULONG cUiMenuGetSpecialMask(MENUITEM *pMenuItem) { ULONG Mask; Mask = 0; if (pMenuItem != NULL) { Mask = (ULONG)pMenuItem->SpecialMask0; Mask |= (ULONG)pMenuItem->SpecialMask1 << 8; Mask |= (ULONG)pMenuItem->SpecialMask2 << 16; Mask |= (ULONG)pMenuItem->SpecialMask3 << 24; } return (Mask); } UBYTE* cUiMenuGetIconImage(UBYTE No) { UBYTE *Image; Image = NULL; if (No < (Icons.ItemsX * Icons.ItemsY)) { Image = (UBYTE*)&Icons.Data[No * Icons.ItemPixelsX * (Icons.ItemPixelsY / 8)]; } return (Image); } ULONG cUiMenuMotherId(ULONG Id,UBYTE Level) { ULONG MotherIdMask; MotherIdMask = 0xFFFFFFFFL >> ((8 - Level) * 4); MotherIdMask |= 0xFFFFFFFFL << ((Level + 1) * 4); return (Id & MotherIdMask); } UBYTE cUiMenuIdValid(MENUFILE *pMenuFile,ULONG Id) { ULONG SpecialMask; ULONG MotherId; UBYTE Level; UBYTE Result; Result = FALSE; Level = pMenuFile->MenuLevel; if (Level) { SpecialMask = pMenuFile->MenuLevels[Level - 1].SpecialFlags; MotherId = pMenuFile->MenuLevels[Level - 1].Id; if ((SpecialMask & MENU_SKIP_THIS_MOTHER_ID)) { MotherId &= ~(0x0000000F << ((Level - 1) * 4)); SpecialMask >>= 28; MotherId |= (SpecialMask << ((Level - 1) * 4)); } if (MotherId == cUiMenuMotherId(Id,Level)) { Id >>= (Level * 4); if ((Id & 0x0000000F) && (!(Id & 0xFFFFFFF0))) { Result = TRUE; } } } else { if ((Id & 0x0000000F) && (!(Id & 0xFFFFFFF0))) { Result = TRUE; } } return (Result); } UBYTE cUiMenuGetNoOfMenus(MENU *pMenu,MENUFILE *pMenuFile) { ULONG MenuId; UBYTE NoOfMenus; UBYTE Index; NoOfMenus = 0; for (Index = 0;Index < pMenu->Items;Index++) { MenuId = cUiMenuGetId(&pMenu->Data[Index]); if (cUiMenuIdValid(pMenuFile,MenuId) == TRUE) { if ((cUiMenuGetSpecialMask(&pMenu->Data[Index]) & MENU_ONLY_BT_ON)) { // BT module must be on if (!(IOMapUi.BluetoothState & BT_STATE_OFF)) { // Yes NoOfMenus++; } } else { if ((cUiMenuGetSpecialMask(&pMenu->Data[Index]) & MENU_ONLY_DATALOG_ENABLED)) { // Datalog menu must be enabled if (VarsUi.NVData.DatalogEnabled) { // Yes NoOfMenus++; } } else { // No restrictions NoOfMenus++; } } } } return (NoOfMenus); } UBYTE cUiGetMenuItemIndex(MENU *pMenu,MENUFILE *pMenuFile,UBYTE No) { ULONG MenuId; UBYTE NoOfMenus; UBYTE Index; UBYTE TmpIndex = 0; NoOfMenus = 0; for (Index = 0;(Index < pMenu->Items) && (No != NoOfMenus);Index++) { MenuId = cUiMenuGetId(&pMenu->Data[Index]); if (cUiMenuIdValid(pMenuFile,MenuId) == TRUE) { if ((cUiMenuGetSpecialMask(&pMenu->Data[Index]) & MENU_ONLY_BT_ON)) { // BT module must be on if (!(IOMapUi.BluetoothState & BT_STATE_OFF)) { // Yes TmpIndex = Index; NoOfMenus++; } } else { if ((cUiMenuGetSpecialMask(&pMenu->Data[Index]) & MENU_ONLY_DATALOG_ENABLED)) { // Datalog menu must be enabled if (VarsUi.NVData.DatalogEnabled) { // Yes TmpIndex = Index; NoOfMenus++; } } else { // No restrictions TmpIndex = Index; NoOfMenus++; } } } } if (No != NoOfMenus) { Index = TmpIndex + 1; } return (Index); } UBYTE cUiMenuGetNo(MENU *pMenu,ULONG Id,UBYTE Level) { ULONG MenuId; ULONG MotherId; UBYTE Index; UBYTE No; UBYTE NoOfItems; No = 0; NoOfItems = 0; MotherId = cUiMenuMotherId(Id,Level); for (Index = 0;(Index < pMenu->Items) && (No == 0);Index++) { MenuId = cUiMenuGetId(&pMenu->Data[Index]); // Scanning all id's until No is found if (!(MenuId >> ((Level + 1) * 4))) { // MenuId is above or on actual level if (((MenuId >> (Level * 4)) & 0x0000000F)) { // MenuId is on actual level if (MotherId == cUiMenuMotherId(MenuId,Level)) { // Same mother id NoOfItems++; if (MenuId == Id) { No = NoOfItems; } } } } } return (No); } void cUiUpdateStatus(void) { UWORD Tmp; UWORD Hyst; UWORD *pTmp; UBYTE Pointer; if (++VarsUi.UpdateCounter >= RUN_STATUS_CHANGE_TIME) { VarsUi.UpdateCounter = 0; // Update running status icon pointer if (++VarsUi.Running >= 12) { VarsUi.Running = 0; } // Get battery voltage limits if ((IoFromAvr.Battery & 0x8000)) { IOMapUi.Rechargeable = 1; pTmp = (UWORD*)RechargeableLimits; Hyst = RECHARGEABLELIMITHYST; } else { IOMapUi.Rechargeable = 0; pTmp = (UWORD*)BatteryLimits; Hyst = BATTERYLIMITHYST; } // Calculate battery voltage Tmp = IoFromAvr.Battery & 0x03FF; Tmp = (UWORD)((float)Tmp * BATTERY_COUNT_TO_MV); IOMapUi.BatteryVoltage = Tmp; // Find new battery state Pointer = 0; while ((Tmp > pTmp[Pointer]) && (Pointer < BATTERYLIMITS)) { Pointer++; } // Change battery state if (Pointer != IOMapUi.BatteryState) { if (Pointer > IOMapUi.BatteryState) { if (Tmp > (pTmp[IOMapUi.BatteryState] + Hyst)) { IOMapUi.BatteryState = Pointer; } } else { IOMapUi.BatteryState = Pointer; } } // Control toggle and bitmap if (IOMapUi.BatteryState) { VarsUi.BatteryToggle = 0; VarsUi.LowBatt = 0; } else { if (VarsUi.LowBatt < 255) { VarsUi.LowBatt++; } if (VarsUi.BatteryToggle) { VarsUi.BatteryToggle = 0; } else { VarsUi.BatteryToggle = 1; } } // Ensure frequently status updates IOMapUi.Flags |= UI_UPDATE; } if ((IOMapUi.Flags & UI_ENABLE_STATUS_UPDATE)) { if ((IOMapUi.Flags & UI_UPDATE) || (IOMapUi.Flags & UI_REDRAW_STATUS)) { VarsUi.ErrorTimer = 0; pMapDisplay->pStatusText = (UBYTE*)VarsUi.StatusText; // Status line update nessesary if (IOMapUi.BatteryState < Status.ItemsX) { // Update battery status icons if (IoFromAvr.Battery & 0x8000) { VarsUi.NewStatusIcons[STATUSICON_BATTERY] = STATUS_NO_RECHARGEABLE_0 + IOMapUi.BatteryState + VarsUi.BatteryToggle; } else { VarsUi.NewStatusIcons[STATUSICON_BATTERY] = STATUS_NO_BATTERY_0 + IOMapUi.BatteryState + VarsUi.BatteryToggle; } } // Update bluetooth status icons if ((IOMapUi.BluetoothState & (BT_STATE_VISIBLE | BT_STATE_CONNECTED | BT_STATE_OFF)) < Status.ItemsX) { VarsUi.NewStatusIcons[STATUSICON_BLUETOOTH] = STATUS_NO_BLUETOOTH_0 + (IOMapUi.BluetoothState & (BT_STATE_VISIBLE | BT_STATE_CONNECTED | BT_STATE_OFF)); } // Update usb status icons if (IOMapUi.UsbState < 6) { VarsUi.NewStatusIcons[STATUSICON_USB] = STATUS_NO_USB_0 + IOMapUi.UsbState; } // Update running status icons if (IOMapUi.RunState == FALSE) { VarsUi.Running = 0; } VarsUi.NewStatusIcons[STATUSICON_VM] = STATUS_NO_RUNNING_0 + VarsUi.Running; // Update only changed status icons for (Pointer = 0;Pointer < STATUSICONS;Pointer++) { if ((pMapDisplay->StatusIcons[Pointer] != VarsUi.NewStatusIcons[Pointer])) { pMapDisplay->StatusIcons[Pointer] = VarsUi.NewStatusIcons[Pointer]; pMapDisplay->UpdateMask |= STATUSICON_BIT(Pointer); } } if ((IOMapUi.Flags & UI_REDRAW_STATUS)) { // Entire status line needs to be redrawed if (pMapComm->BrickData.Name[0]) { for (Pointer = 0;Pointer < STATUSTEXT_SIZE;Pointer++) { VarsUi.StatusText[Pointer] = pMapComm->BrickData.Name[Pointer]; } VarsUi.StatusText[Pointer] = 0; } pMapDisplay->EraseMask |= SPECIAL_BIT(STATUSTEXT); pMapDisplay->UpdateMask |= SPECIAL_BIT(STATUSTEXT); pMapDisplay->UpdateMask |= (SPECIAL_BIT(TOPLINE) | STATUSICON_BITS); } // Clear update flag IOMapUi.Flags &= ~UI_REDRAW_STATUS; IOMapUi.Flags &= ~UI_UPDATE; } } else { pMapDisplay->UpdateMask &= ~(STATUSICON_BITS | SPECIAL_BIT(TOPLINE) | SPECIAL_BIT(STATUSTEXT)); } } void cUiMenuCallFunction(UBYTE Function,UBYTE Parameter) { if (Function) { VarsUi.Function = Function; VarsUi.Parameter = Parameter; } } void cUiMenuNextFile(void) { MENU *pTmpMenu; pTmpMenu = (MENU*)cUiGetMenuPointer(VarsUi.pMenuLevel->NextFileNo); if (pTmpMenu != NULL) { if (VarsUi.MenuFileLevel < (MENUFILELEVELS - 1)) { VarsUi.MenuFileLevel++; VarsUi.MenuFiles[VarsUi.MenuFileLevel].FileId = VarsUi.pMenuLevel->NextFileNo; VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel = 0; VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevels[VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel].ItemIndex = VarsUi.pMenuLevel->NextMenuNo; IOMapUi.pMenu = pTmpMenu; } } } void cUiMenuPrevFile(void) { if (VarsUi.MenuFileLevel) { VarsUi.MenuFileLevel--; IOMapUi.pMenu = (MENU*)cUiGetMenuPointer(VarsUi.MenuFiles[VarsUi.MenuFileLevel].FileId); } } void cUiMenuNext(void) { if (VarsUi.pMenuFile->MenuLevel < (MENULEVELS - 1)) { VarsUi.pMenuFile->MenuLevel++; VarsUi.pMenuFile->MenuLevels[VarsUi.pMenuFile->MenuLevel].ItemIndex = VarsUi.pMenuLevel->NextMenuNo; } } void cUiMenuPrev(void) { if (VarsUi.pMenuFile->MenuLevel) { VarsUi.pMenuFile->MenuLevel--; } } void cUiMenuEnter(void) { // Call function with parameter (if pressent) if (!(VarsUi.pMenuLevel->SpecialFlags & MENU_INIT_CALLS)) { cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,VarsUi.pMenuLevel->Parameter); } if (VarsUi.EnterOnlyCalls != TRUE) { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_ENTER_LEAVES_MENUFILE)) { cUiMenuPrevFile(); } else { // Load new menu file (if pressent) if (VarsUi.pMenuLevel->NextFileNo) { cUiMenuNextFile(); } else { // Activate next menu level (if pressent) if (VarsUi.pMenuLevel->NextMenuNo) { cUiMenuNext(); } } } IOMapUi.State = NEXT_MENU; } else { VarsUi.EnterOnlyCalls = FALSE; IOMapUi.State = DRAW_MENU; } } void cUiMenuExit(void) { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_CALLS)) { // Call function with parameter (if pressent) if (!(VarsUi.pMenuLevel->SpecialFlags & MENU_INIT_CALLS)) { cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,VarsUi.pMenuLevel->Parameter); } } // Call function with 0xFF (if ordered) if ((VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_CALLS_WITH_FF)) { cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,MENU_EXIT); } if (VarsUi.ExitOnlyCalls != TRUE) { if ((VarsUi.pMenuFile->MenuLevel) && (!(VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_LEAVES_MENUFILE))) { if (!(VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_LOAD_MENU)) { cUiMenuPrev(); if ((VarsUi.pMenuLevel->SpecialFlags & MENU_BACK_TWICE)) { if (VarsUi.pMenuFile->MenuLevel) { cUiMenuPrev(); } VarsUi.SecondTime = FALSE; } if ((VarsUi.pMenuLevel->SpecialFlags & MENU_BACK_THREE_TIMES)) { if (VarsUi.pMenuFile->MenuLevel) { cUiMenuPrev(); } if (VarsUi.pMenuFile->MenuLevel) { cUiMenuPrev(); } VarsUi.SecondTime = FALSE; } } else { VarsUi.EnterOnlyCalls = FALSE; VarsUi.ExitOnlyCalls = FALSE; if (VarsUi.pMenuLevel->NextFileNo) { cUiMenuNextFile(); } else { // Activate next menu level (if pressent) if (VarsUi.pMenuLevel->NextMenuNo) { cUiMenuNext(); } } } if ((VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_LOAD_POINTER)) { VarsUi.pMenuFile->MenuLevels[VarsUi.pMenuFile->MenuLevel].ItemIndex = (UBYTE)((VarsUi.pMenuLevel->SpecialFlags) >> 24) & 0x0F; } } else { if (!(VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_LOAD_MENU)) { cUiMenuPrevFile(); } else { VarsUi.EnterOnlyCalls = FALSE; VarsUi.ExitOnlyCalls = FALSE; if (VarsUi.pMenuLevel->NextFileNo) { cUiMenuNextFile(); } else { // Activate next menu level (if pressent) if (VarsUi.pMenuLevel->NextMenuNo) { cUiMenuNext(); } } } } IOMapUi.State = NEXT_MENU; } else { VarsUi.ExitOnlyCalls = FALSE; IOMapUi.State = DRAW_MENU; } } void cUiLoadLevel(UBYTE FileLevel,UBYTE MenuLevel,UBYTE MenuIndex) { UBYTE Tmp; VarsUi.MenuFileLevel = FileLevel; VarsUi.MenuFiles[FileLevel].MenuLevel = MenuLevel; VarsUi.MenuFiles[FileLevel].MenuLevels[MenuLevel].ItemIndex = MenuIndex; IOMapUi.pMenu = (MENU*)cUiGetMenuPointer(VarsUi.MenuFiles[VarsUi.MenuFileLevel].FileId); VarsUi.pMenuFile = &VarsUi.MenuFiles[VarsUi.MenuFileLevel]; VarsUi.pMenuLevel = &VarsUi.pMenuFile->MenuLevels[VarsUi.pMenuFile->MenuLevel]; // Count no of menues on current level VarsUi.pMenuLevel->Items = cUiMenuGetNoOfMenus(IOMapUi.pMenu,VarsUi.pMenuFile); if (VarsUi.pMenuLevel->Items) { // if items > 0 -> prepare allways center icon Tmp = cUiGetMenuItemIndex(IOMapUi.pMenu,VarsUi.pMenuFile,VarsUi.pMenuLevel->ItemIndex); if (VarsUi.pMenuItem != &IOMapUi.pMenu->Data[Tmp - 1]) { VarsUi.pMenuItem = &IOMapUi.pMenu->Data[Tmp - 1]; VarsUi.SecondTime = FALSE; } // Save center menu item parameters VarsUi.pMenuLevel->Id = cUiMenuGetId(VarsUi.pMenuItem); VarsUi.pMenuLevel->IconImageNo = VarsUi.pMenuItem->IconImageNo; VarsUi.pMenuLevel->IconText = VarsUi.pMenuItem->IconText; VarsUi.pMenuLevel->SpecialFlags = cUiMenuGetSpecialMask(VarsUi.pMenuItem); VarsUi.pMenuLevel->FunctionNo = VarsUi.pMenuItem->FunctionIndex; VarsUi.pMenuLevel->Parameter = VarsUi.pMenuItem->FunctionParameter; VarsUi.pMenuLevel->NextFileNo = VarsUi.pMenuItem->FileLoadNo; VarsUi.pMenuLevel->NextMenuNo = VarsUi.pMenuItem->NextMenu; } } #include "Functions.inl" void cUiInit(void* pHeader) { pHeaders = pHeader; VarsUi.Initialized = FALSE; IOMapUi.BluetoothState = BT_STATE_OFF; IOMapUi.UsbState = 0; IOMapUi.State = INIT_DISPLAY; } void cUiCtrl(void) { UBYTE Tmp; // Testcode for low battery voltage /* if ((pMapInput->Inputs[0].InvalidData != INVALID_DATA) && (pMapInput->Inputs[0].ADRaw < 500)) { if (VarsUi.LowBatt < 255) { VarsUi.LowBatt++; } } else { VarsUi.LowBatt = 0; } */ // // Testcode for BT connect request /* if ((pMapInput->Inputs[0].InvalidData != INVALID_DATA) && (pMapInput->Inputs[0].ADRaw < 500)) { IOMapUi.BluetoothState |= BT_CONNECT_REQUEST | BT_PIN_REQUEST; } */ // // Testcode for BT error attention /* if ((pMapInput->Inputs[0].InvalidData != INVALID_DATA) && (pMapInput->Inputs[0].ADRaw < 500)) { IOMapUi.Error = LR_UNKOWN_ADDR; IOMapUi.BluetoothState |= BT_ERROR_ATTENTION; } */ // // Testcode for execute program /* if ((pMapInput->Inputs[0].InvalidData != INVALID_DATA) && (pMapInput->Inputs[0].ADRaw < 500)) { if ((!(IOMapUi.Flags & UI_EXECUTE_LMS_FILE)) && (IOMapUi.State > INIT_MENU)) { strcpy((char*)IOMapUi.LMSfilename,"Untitled-1.rxe"); IOMapUi.Flags |= UI_EXECUTE_LMS_FILE; } } */ // // Testcode for force off /* if ((pMapInput->Inputs[0].InvalidData != INVALID_DATA) && (pMapInput->Inputs[0].ADRaw < 500) && (VarsUi.Initialized == TRUE)) { IOMapUi.ForceOff = TRUE; } */ // VarsUi.CRPasskey++; VarsUi.ButtonTimer++; VarsUi.OBPTimer++; switch (IOMapUi.State) { case INIT_DISPLAY : // Load font and icons { // pMapLoader->pFunc(DELETEUSERFLASH,NULL,NULL,NULL); VarsUi.Initialized = FALSE; IOMapUi.Flags = UI_BUSY; IOMapUi.RunState = 1; IOMapUi.BatteryState = 0; IOMapUi.Error = 0; IOMapUi.ForceOff = FALSE; VarsUi.LowBatt = 0; VarsUi.LowBattHasOccured = 0; pMapDisplay->pFont = (FONT*)&Font; pMapDisplay->pStatusIcons = (ICON*)&Status; pMapDisplay->pStatusText = (UBYTE*)VarsUi.StatusText; pMapDisplay->pStepIcons = (ICON*)&Step; VarsUi.State = 0; VarsUi.Pointer = 0; VarsUi.Timer = CONFIG_INTRO ? -INTRO_START_TIME : 0; VarsUi.FNOFState = 0; VarsUi.FBState = 0; VarsUi.UpdateCounter = 0; VarsUi.Running = 0; VarsUi.BatteryToggle = 0; VarsUi.GUSState = 0; IOMapUi.pMenu = (MENU*)cUiGetMenuPointer(0); IOMapUi.State = CONFIG_INTRO ? INIT_INTRO : INIT_WAIT; pMapDisplay->EraseMask = SCREEN_BIT(SCREEN_BACKGROUND); pMapDisplay->pBitmaps[BITMAP_1] = CONFIG_INTRO ? (BMPMAP*)Intro[VarsUi.Pointer] : (BMPMAP*)&RCXintro_16; pMapDisplay->UpdateMask = BITMAP_BIT(BITMAP_1); pMapDisplay->Flags |= DISPLAY_ON; cUiNVRead(); IOMapUi.Volume = VarsUi.NVData.VolumeStep; IOMapUi.SleepTimeout = PowerOffTimeSteps[VarsUi.NVData.PowerdownCode]; } break; #if CONFIG_INTRO case INIT_LOW_BATTERY : { if (++VarsUi.Timer >= (INTRO_LOWBATT_TIME)) { VarsUi.LowBattHasOccured = 2; pMapDisplay->EraseMask = SCREEN_BIT(SCREEN_BACKGROUND); pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)Intro[VarsUi.Pointer]; pMapDisplay->UpdateMask = BITMAP_BIT(BITMAP_1); IOMapUi.Flags &= ~UI_ENABLE_STATUS_UPDATE; VarsUi.State = 0; VarsUi.Pointer = 0; VarsUi.Timer = -INTRO_START_TIME; IOMapUi.State = INIT_INTRO; } } break; case INIT_INTRO : { if (VarsUi.LowBattHasOccured == 1) { IOMapUi.Flags |= UI_ENABLE_STATUS_UPDATE; IOMapUi.Flags |= UI_UPDATE; IOMapUi.Flags |= UI_REDRAW_STATUS; VarsUi.Timer = 0; IOMapUi.State = INIT_LOW_BATTERY; } else { if (VarsUi.LowBattHasOccured == 0) { if (VarsUi.LowBatt) { pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)&LowBattery; pMapDisplay->UpdateMask = BITMAP_BIT(BITMAP_1); VarsUi.LowBattHasOccured = 1; } } if (++VarsUi.Timer >= (INTRO_SHIFT_TIME)) { switch (VarsUi.State) { case 0 : { pMapDisplay->Flags &= ~DISPLAY_REFRESH; VarsUi.State++; } break; case 1 : { if ((pMapDisplay->Flags & DISPLAY_REFRESH_DISABLED)) { if (VarsUi.Pointer < NO_OF_INTROBITMAPS) { pMapDisplay->EraseMask |= BITMAP_BIT(BITMAP_1); pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)Intro[VarsUi.Pointer]; pMapDisplay->UpdateMask = BITMAP_BIT(BITMAP_1); if (VarsUi.Pointer == 11) { sprintf((char*)pMapSound->SoundFilename,"%s.%s",(char*)UI_STARTUP_SOUND,(char*)TXT_FILE_EXT[FILETYPE_SOUND]); pMapSound->Volume = IOMapUi.Volume; pMapSound->Mode = SOUND_ONCE; pMapSound->Flags |= SOUND_UPDATE; } VarsUi.Pointer++; } else { pMapDisplay->Flags |= DISPLAY_REFRESH; IOMapUi.State = INIT_WAIT; } VarsUi.State++; } } break; default : { if (!(pMapDisplay->UpdateMask & BITMAP_BIT(BITMAP_1))) { pMapDisplay->Flags |= DISPLAY_REFRESH; VarsUi.Timer = 0; VarsUi.State = 0; } } break; } } } } break; #endif /* CONFIG_INTRO */ case INIT_WAIT : { if (++VarsUi.Timer >= INTRO_STOP_TIME) { pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_BACKGROUND); IOMapUi.State = INIT_MENU; } } break; case INIT_MENU : { // Restart menu system VarsUi.Function = 0; VarsUi.MenuFileLevel = 0; cUiLoadLevel(0,0,1); cUiLoadLevel(0,1,1); VarsUi.EnterOnlyCalls = FALSE; VarsUi.ExitOnlyCalls = FALSE; IOMapUi.State = NEXT_MENU; } break; case NEXT_MENU : // prepare icons { // Init various variables VarsUi.State = 0; // Init icon pointers pMapDisplay->pMenuIcons[MENUICON_LEFT] = NULL; pMapDisplay->pMenuIcons[MENUICON_CENTER] = NULL; pMapDisplay->pMenuIcons[MENUICON_RIGHT] = NULL; cUiLoadLevel(VarsUi.MenuFileLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevels[VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel].ItemIndex); // Find menu icons if (VarsUi.pMenuLevel->Items) { // Prepare center icon pMapDisplay->pMenuIcons[MENUICON_CENTER] = cUiMenuGetIconImage(VarsUi.pMenuLevel->IconImageNo); pMapDisplay->pMenuText = VarsUi.pMenuLevel->IconText; if (VarsUi.pMenuLevel->Items == 2) { // if 2 menues -> prepare left or right icon if (VarsUi.pMenuLevel->ItemIndex == 1) { Tmp = cUiGetMenuItemIndex(IOMapUi.pMenu,VarsUi.pMenuFile,2); if (Tmp) { Tmp--; pMapDisplay->pMenuIcons[MENUICON_RIGHT] = cUiMenuGetIconImage(IOMapUi.pMenu->Data[Tmp].IconImageNo); } } else { Tmp = cUiGetMenuItemIndex(IOMapUi.pMenu,VarsUi.pMenuFile,1); if (Tmp) { Tmp--; pMapDisplay->pMenuIcons[MENUICON_LEFT] = cUiMenuGetIconImage(IOMapUi.pMenu->Data[Tmp].IconImageNo); } } } if (VarsUi.pMenuLevel->Items > 2) { // if more menues -> prepare left and right icon if (VarsUi.pMenuLevel->ItemIndex > 1) { Tmp = VarsUi.pMenuLevel->ItemIndex -1; } else { Tmp = VarsUi.pMenuLevel->Items; } Tmp = cUiGetMenuItemIndex(IOMapUi.pMenu,VarsUi.pMenuFile,Tmp); if (Tmp) { Tmp--; pMapDisplay->pMenuIcons[MENUICON_LEFT] = cUiMenuGetIconImage(IOMapUi.pMenu->Data[Tmp].IconImageNo); } if (VarsUi.pMenuLevel->ItemIndex < VarsUi.pMenuLevel->Items) { Tmp = VarsUi.pMenuLevel->ItemIndex + 1; } else { Tmp = 1; } Tmp = cUiGetMenuItemIndex(IOMapUi.pMenu,VarsUi.pMenuFile,Tmp); if (Tmp) { Tmp--; pMapDisplay->pMenuIcons[MENUICON_RIGHT] = cUiMenuGetIconImage(IOMapUi.pMenu->Data[Tmp].IconImageNo); } } } if ((VarsUi.pMenuLevel->SpecialFlags & MENU_ENTER_ONLY_CALLS)) { VarsUi.EnterOnlyCalls = TRUE; } if ((VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_ONLY_CALLS)) { VarsUi.ExitOnlyCalls = TRUE; } IOMapUi.State = DRAW_MENU; } break; case DRAW_MENU : // If no function active -> erase screen, draw statusline and menu icons { if (VarsUi.Function) { // Function active if (VarsUi.Function < FUNC_NO_MAX) { if (Functions[VarsUi.Function](VarsUi.Parameter) == 0) { VarsUi.Function = 0; } } else { VarsUi.Function = 0; } } else { // function inactive - erase screen if (!(VarsUi.pMenuLevel->SpecialFlags & MENU_LEAVE_BACKGROUND)) { pMapDisplay->EraseMask |= SCREEN_BIT(SCREEN_LARGE); // Draw only icons, frame and icon text pMapDisplay->UpdateMask = (MENUICON_BITS | SPECIAL_BIT(FRAME_SELECT) | SPECIAL_BIT(MENUTEXT)); pMapDisplay->TextLinesCenterFlags = 0; } else { pMapDisplay->EraseMask |= (SPECIAL_BIT(MENUTEXT) | MENUICON_BITS); // Draw icons, frame and icon text pMapDisplay->UpdateMask |= (MENUICON_BITS | SPECIAL_BIT(FRAME_SELECT) | SPECIAL_BIT(MENUTEXT)); } // Draw status IOMapUi.Flags |= (UI_ENABLE_STATUS_UPDATE | UI_UPDATE | UI_REDRAW_STATUS); if ((VarsUi.pMenuLevel->SpecialFlags & MENU_INIT_CALLS_WITH_0) && (VarsUi.SecondTime == FALSE)) { VarsUi.SecondTime = TRUE; cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,MENU_INIT); } else { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_INIT_CALLS_WITH_1) && (VarsUi.SecondTime == FALSE)) { VarsUi.SecondTime = TRUE; cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,MENU_INIT_ALTERNATIVE); } else { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_INIT_CALLS) && (VarsUi.SecondTime == FALSE)) { VarsUi.SecondTime = TRUE; cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,VarsUi.pMenuLevel->Parameter); } else { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_AUTO_PRESS_ENTER)) { IOMapUi.State = ENTER_PRESSED; } else { IOMapUi.State = TEST_BUTTONS; } } } } } } break; case TEST_BUTTONS : // Test buttons to execute new functions and new menus { if (VarsUi.Initialized == FALSE) { VarsUi.Initialized = TRUE; IOMapUi.Flags &= ~UI_BUSY; } switch (cUiReadButtons()) { case BUTTON_LEFT : { IOMapUi.State = LEFT_PRESSED; } break; case BUTTON_RIGHT : { IOMapUi.State = RIGHT_PRESSED; } break; case BUTTON_ENTER : { IOMapUi.State = ENTER_PRESSED; } break; case BUTTON_EXIT : { if (!(VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_DISABLE)) { IOMapUi.State = EXIT_PRESSED; } } break; } } break; case LEFT_PRESSED : { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_LEFT_RIGHT_AS_CALL)) { cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,MENU_LEFT); } else { VarsUi.SecondTime = FALSE; VarsUi.EnterOnlyCalls = FALSE; VarsUi.ExitOnlyCalls = FALSE; if (VarsUi.pMenuLevel->ItemIndex > 1) { VarsUi.pMenuLevel->ItemIndex--; } else { if (VarsUi.pMenuLevel->Items > 2) { VarsUi.pMenuLevel->ItemIndex = VarsUi.pMenuLevel->Items; } } } IOMapUi.State = NEXT_MENU; } break; case RIGHT_PRESSED : { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_LEFT_RIGHT_AS_CALL)) { cUiMenuCallFunction(VarsUi.pMenuLevel->FunctionNo,MENU_RIGHT); } else { VarsUi.SecondTime = FALSE; VarsUi.EnterOnlyCalls = FALSE; VarsUi.ExitOnlyCalls = FALSE; if (VarsUi.pMenuLevel->ItemIndex < VarsUi.pMenuLevel->Items) { VarsUi.pMenuLevel->ItemIndex++; } else { if (VarsUi.pMenuLevel->Items > 2) { VarsUi.pMenuLevel->ItemIndex = 1; } } } IOMapUi.State = NEXT_MENU; } break; case ENTER_PRESSED : { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_ENTER_ACT_AS_EXIT)) { cUiMenuExit(); } else { cUiMenuEnter(); } } break; case EXIT_PRESSED : { if ((VarsUi.pMenuLevel->SpecialFlags & MENU_EXIT_ACT_AS_ENTER)) { cUiMenuEnter(); } else { cUiMenuExit(); } } break; case CONNECT_REQUEST : { if (cUiBTConnectRequest(MENU_INIT) == 0) { IOMapUi.BluetoothState &= ~BT_CONNECT_REQUEST; cUiLoadLevel(0,1,1); IOMapUi.State = NEXT_MENU; IOMapUi.Flags &= ~UI_BUSY; } } break; case EXECUTE_FILE : { cUiLoadLevel(0,1,1); cUiMenuEnter(); cUiLoadLevel(VarsUi.MenuFileLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevels[VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel].ItemIndex); cUiMenuEnter(); cUiLoadLevel(VarsUi.MenuFileLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevels[VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel].ItemIndex); cUiMenuEnter(); cUiLoadLevel(VarsUi.MenuFileLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel,VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevels[VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel].ItemIndex); VarsUi.Function = 0; VarsUi.State = 0; VarsUi.Pointer = 0; VarsUi.FNOFState = 0; VarsUi.FBState = 0; VarsUi.GUSState = 0; strcpy((char*)VarsUi.SelectedFilename,(char*)IOMapUi.LMSfilename); IOMapUi.State = EXECUTING_FILE; VarsUi.FileType = FILETYPE_LMS; cUiFileRun(MENU_INIT); } break; case EXECUTING_FILE : { if (cUiFileRun(MENU_RUN) == 0) { IOMapUi.Flags &= ~UI_EXECUTE_LMS_FILE; IOMapUi.State = NEXT_MENU; } } break; case LOW_BATTERY : { if (DISPLAY_IDLE) { if (cUiReadButtons() != BUTTON_NONE) { pMapDisplay->Flags &= ~DISPLAY_POPUP; pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)VarsUi.LowBattSavedBitmap; IOMapUi.State = VarsUi.LowBattSavedState; IOMapUi.Flags &= ~UI_BUSY; } } } break; case BT_ERROR : { switch (IOMapUi.Error) { case LR_COULD_NOT_SAVE : { Tmp = TXT_FB_BT_ERROR_LR_COULD_NOT_SAVE_1; } break; case LR_STORE_IS_FULL : { Tmp = TXT_FB_BT_ERROR_LR_STORE_IS_FULL_1; } break; default : { Tmp = TXT_FB_BT_ERROR_LR_UNKOWN_ADDR_1; } break; } if (!cUiFeedback((BMPMAP*)&Fail,Tmp,Tmp + 1,DISPLAY_SHOW_ERROR_TIME)) { IOMapUi.BluetoothState &= ~BT_ERROR_ATTENTION; cUiLoadLevel(0,1,1); IOMapUi.State = NEXT_MENU; IOMapUi.Flags &= ~UI_BUSY; } } break; } // Check for low battery voltage if (VarsUi.LowBatt >= LOW_BATT_THRESHOLD) { if (!VarsUi.LowBattHasOccured) { if (DISPLAY_IDLE) { if (!(IOMapUi.Flags & UI_BUSY)) { pMapDisplay->Flags |= DISPLAY_POPUP; VarsUi.LowBattHasOccured = 1; VarsUi.LowBattSavedState = IOMapUi.State; VarsUi.LowBattSavedBitmap = (UBYTE*)pMapDisplay->pBitmaps[BITMAP_1]; pMapDisplay->pBitmaps[BITMAP_1] = (BMPMAP*)&LowBattery; pMapDisplay->UpdateMask = BITMAP_BIT(BITMAP_1); IOMapUi.Flags |= UI_REDRAW_STATUS; IOMapUi.Flags |= UI_BUSY; IOMapUi.State = LOW_BATTERY; } } } } // Check for incomming BT connection requests if ((IOMapUi.BluetoothState & BT_CONNECT_REQUEST) && (!(IOMapUi.Flags & UI_BUSY))) { IOMapUi.Flags |= UI_BUSY; IOMapUi.State = CONNECT_REQUEST; } // Check for BT errors if ((IOMapUi.BluetoothState & BT_ERROR_ATTENTION) && (!(IOMapUi.Flags & UI_BUSY))) { IOMapUi.Flags |= UI_BUSY; IOMapUi.State = BT_ERROR; } // Check for incomming execute program if ((IOMapUi.Flags & UI_EXECUTE_LMS_FILE) && (!(IOMapUi.Flags & UI_BUSY))) { // Reset power down timer IOMapUi.Flags |= UI_RESET_SLEEP_TIMER; // Set state and busy IOMapUi.Flags |= UI_BUSY; IOMapUi.State = EXECUTE_FILE; } // Check for power timeout if ((IOMapUi.Flags & UI_RESET_SLEEP_TIMER)) { IOMapUi.Flags &= ~UI_RESET_SLEEP_TIMER; IOMapUi.SleepTimer = 0; VarsUi.SleepTimer = 0; } if (IOMapUi.SleepTimeout) { if (++VarsUi.SleepTimer >= 60000) { VarsUi.SleepTimer = 0; if (++IOMapUi.SleepTimer >= IOMapUi.SleepTimeout) { IOMapUi.ForceOff = TRUE; } } } else { IOMapUi.Flags |= UI_RESET_SLEEP_TIMER; } // Check for "long prees on exit" power off if ((pMapButton->State[BTN1] & LONG_PRESSED_EV) && (pMapCmd->ProgStatus != PROG_RUNNING) && (VarsUi.Initialized == TRUE) && (VarsUi.State == 0)) { IOMapUi.ForceOff = TRUE; } // Check for "force" off if (IOMapUi.ForceOff != FALSE) { IOMapUi.ForceOff = FALSE; VarsUi.Function = FUNC_NO_OFF; VarsUi.Parameter = MENU_INIT; VarsUi.State = 0; IOMapUi.State = DRAW_MENU; } // Update status line cUiUpdateStatus(); } void cUiExit(void) { VarsUi.Initialized = FALSE; IOMapUi.State = INIT_DISPLAY; } nxt-firmware-1.29.7/src/c_ui.h000066400000000000000000000544001466344546000161330ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dktochpe $ // // Revision date $Date:: 10/21/08 12:08p $ // // Filename $Workfile:: c_ui.h $ // // Version $Revision:: 10 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_ui.h $ // // Platform C // #ifndef C_UI #define C_UI #define DATALOGENABLED 1 // 1 == Datalog enable #define NO_OF_FEEDBACK_CHARS 12 // Chars left when bitmap also showed #define SIZE_OF_CURSOR 16 // Bitmap size of cursor (header + 8x8 pixels) #define SIZE_OF_PORTBITMAP 11 // Bitmap size of port no (header + 3x8 pixels) #define NO_OF_STATUSICONS 4 // Status icons #define NO_OF_INTROBITMAPS 16 // Intro bitmaps #define INTRO_START_TIME 1000 // Intro startup time [mS] #define INTRO_SHIFT_TIME 100 // Intro inter bitmap time [mS] #define INTRO_STOP_TIME 1000 // Intro stop time [mS] #define INTRO_LOWBATT_TIME 2000 // Low battery show time at power up [mS] #define MAX_VOLUME 4 // Max volume in UI [cnt] #define CHECKBYTE 0x78 // Used to validate NVData #define BATTERY_COUNT_TO_MV 13.848f // Battery count to mV factor [mV/cnt] #define LOW_BATT_THRESHOLD 6 // Low batt conunts before warning #define BUTTON_DELAY_TIME 800 // Delay before first repeat [mS] #define BUTTON_REPEAT_TIME 200 // Repeat time [mS] #define RUN_BITMAP_CHANGE_TIME 125 // Running bimap update time [mS] #define RUN_STATUS_CHANGE_TIME 167 // Running status update time [mS] #define DISPLAY_SHOW_ERROR_TIME 2500 // Error string show time [mS] #define DISPLAY_SHOW_TIME 1500 // Min. response display time [mS] #define DISPLAY_VIEW_UPDATE 200 // Display update time [mS] #define MIN_DISPLAY_UPDATE_TIME 50 // OBP min graphics update time [mS] #define MIN_SENSOR_READ_TIME 100 // Time between sensor reads [mS] #define ARM_WAIT_FOR_POWER_OFF 250 // Time for off command to execute [mS] #define DISPLAY_SHOW_FILENAME_TIME 3000 // Datalog show saves as time [mS] #define DATALOG_DEFAULT_SAMPLE_TIME 100L // Default time between samples [mS] // Menu special flags #define MENU_SKIP_THIS_MOTHER_ID 0x00000001L // Used to seek next common menu (i0000000) // Free #define MENU_ENTER_ACT_AS_EXIT 0x00000004L // Enter button acts as exit button #define MENU_BACK_TWICE 0x00000008L // Exit twice on exit button #define MENU_EXIT_ACT_AS_ENTER 0x00000010L // Exit button acts as enter button #define MENU_LEAVE_BACKGROUND 0x00000020L // Don't erase background at next menu #define MENU_EXIT_CALLS_WITH_FF 0x00000040L // Exit button calls function with MENU_EXIT #define MENU_EXIT_LEAVES_MENUFILE 0x00000080L // Exit leaves menu file #define MENU_INIT_CALLS_WITH_0 0x00000100L // Menu init calls with MENU_INIT #define MENU_LEFT_RIGHT_AS_CALL 0x00000200L // Left calls with MENU_LEFT and right with MENU_RIGHT #define MENU_ENTER_ONLY_CALLS 0x00000400L // Enter calls only it does not change menues #define MENU_EXIT_ONLY_CALLS 0x00000800L // Exit calls only it does not change menues #define MENU_AUTO_PRESS_ENTER 0x00001000L // Enter button is pressed automaticly #define MENU_ENTER_LEAVES_MENUFILE 0x00002000L // Enter leaves menufile #define MENU_INIT_CALLS 0x00004000L // Init calls instead of enter #define MENU_ACCEPT_INCOMMING_REQUEST 0x00008000L // Accept incomming BT connection request #define MENU_BACK_THREE_TIMES 0x00010000L // Exit three times on exit button #define MENU_EXIT_DISABLE 0x00020000L // Disable exit button #define MENU_EXIT_LOAD_POINTER 0x00040000L // Load item index on exit (0i000000) #define MENU_EXIT_CALLS 0x00080000L // Exit calls as enter #define MENU_INIT_CALLS_WITH_1 0x00100000L // Menu init calls with MENU_INIT #define MENU_EXIT_LOAD_MENU 0x00200000L // Exit loads next menu #define MENU_ONLY_BT_ON 0x00400000L // Only valid when bluecore is on #define MENU_ONLY_DATALOG_ENABLED 0x00800000L // Only valid when datalog is enabled // Menu function call parameter #define MENU_SENSOR_EMPTY 0x01 // Empty #define MENU_SENSOR_SOUND_DB 0x02 // Sound sensor dB #define MENU_SENSOR_SOUND_DBA 0x03 // Sound sensor dBA #define MENU_SENSOR_LIGHT 0x04 // Light sensor with flood light #define MENU_SENSOR_LIGHT_AMB 0x05 // Light sensor without flood light #define MENU_SENSOR_TOUCH 0x06 // Touch sensor #define MENU_SENSOR_MOTOR_DEG 0x07 // Motor sensor degrees #define MENU_SENSOR_MOTOR_ROT 0x08 // Motor sensor rotations #define MENU_SENSOR_ULTRASONIC_IN 0x09 // Ultrasonic sensor inch #define MENU_SENSOR_ULTRASONIC_CM 0x0A // Ultrasonic sensor cm #define MENU_SENSOR_IIC_TEMP_C 0x0B // IIC temp sensor celcius #define MENU_SENSOR_IIC_TEMP_F 0x0C // IIC temp sensor fahrenheit #define MENU_SENSOR_COLOR 0x0D // Color sensor #define MENU_SENSOR_INVALID 0x0E // Invalid #define MENU_PORT_EMPTY 0x11 // Port empty #define MENU_PORT_1 0x12 // Port 1 #define MENU_PORT_2 0x13 // Port 2 #define MENU_PORT_3 0x14 // Port 3 #define MENU_PORT_4 0x15 // Port 4 #define MENU_PORT_A 0x16 // Port A #define MENU_PORT_B 0x17 // Port B #define MENU_PORT_C 0x18 // Port C #define MENU_PORT_INVALID 0x19 // Invalid #define MENU_ACTION_EMPTY 0x21 // Empty #define MENU_ACTION_FORWARD_1 0x22 // Forward until #define MENU_ACTION_FORWARD_2 0x23 // Forward 5 #define MENU_ACTION_BACK_LEFT_2 0x24 // Back left 2 #define MENU_ACTION_TURN_LEFT_1 0x25 // Turn left until #define MENU_ACTION_TURN_LEFT_2 0x26 // Turn left 2 #define MENU_ACTION_BACK_RIGHT_1 0x27 // Back right until #define MENU_ACTION_TURN_RIGHT_1 0x28 // Turn right until #define MENU_ACTION_TURN_RIGHT_2 0x29 // Turn right 2 #define MENU_ACTION_BACK_LEFT_1 0x2A // Back left until #define MENU_ACTION_TONE_1 0x2B // Tone 1 #define MENU_ACTION_TONE_2 0x2C // Tone 2 #define MENU_ACTION_BACKWARD_1 0x2D // Backward until #define MENU_ACTION_BACKWARD_2 0x2E // Backward 5 #define MENU_ACTION_BACK_RIGHT_2 0x2F // Back right 2 #define MENU_ACTION_INVALID 0x30 // Invalid #define MENU_WAIT_EMPTY 0x41 // Empty #define MENU_WAIT_LIGHT 0x42 // Light #define MENU_WAIT_SEEK_OBJ 0x43 // Seek obj. #define MENU_WAIT_SOUND 0x44 // Sound #define MENU_WAIT_TOUCH 0x45 // Touch #define MENU_WAIT_1 0x46 // Wait 2 #define MENU_WAIT_2 0x47 // Wait 5 #define MENU_WAIT_3 0x48 // Wait 10 #define MENU_WAIT_DARK 0x49 // Dark #define MENU_WAIT_INVALID 0x4A // Invalid #define MENU_INIT 0x00 // Init #define MENU_INIT_ALTERNATIVE 0x01 // Init alternative #define MENU_DRAW 0xE9 // Draw #define MENU_OFF 0xEA // Off #define MENU_ON 0xEB // On #define MENU_OPEN_STREAM 0xEC // Open stream #define MENU_OVERWRITE 0xED // Overwrite file #define MENU_CALCULATE 0xEE // Calculate #define MENU_ENTER 0xEF // Enter #define MENU_DISCONNECT 0xF0 // Disconnect BT #define MENU_DELETE 0xF1 // Delete #define MENU_SELECT 0xF2 // Select #define MENU_RUN_SILENT 0xF3 // Run without graphics #define MENU_TOGGLE 0xF4 // Toggle #define MENU_CONNECT 0xF5 // Connect BT #define MENU_UPDATE 0xF6 // Update #define MENU_TEXT 0xF7 // Text #define MENU_RUN 0xF8 // Run #define MENU_SEND 0xF9 // Send #define MENU_SAVE 0xFA // Save #define MENU_STOP 0xFB // Stop #define MENU_LOOP 0xFC // Loop #define MENU_LEFT 0xFD // Left #define MENU_RIGHT 0xFE // Right #define MENU_EXIT 0xFF // Exit #define DATALOGPORTS (MENU_PORT_INVALID - MENU_PORT_EMPTY - 1) #define MAX_DATALOGS 9999 // Highest datalog file number #define DATALOGBUFFERSIZE 25 // Largest number of characters buffered before flash write #define MENULEVELS 10 // Max no of levels in one file (8 + 2 virtual) #define MENUFILELEVELS 3 // Max deept in menu file pool typedef struct // VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevels[VarsUi.MenuLevel]. { ULONG Id; // Menu item id UBYTE *IconText; // Menu item icon text pointer ULONG SpecialFlags; // Menu item special behaivor UBYTE IconImageNo; // Menu item icon image no UBYTE FunctionNo; // Menu item function call no (0 = none) UBYTE Parameter; // Menu item function call parameter UBYTE NextFileNo; // Menu item next menu file no (0 = none) UBYTE NextMenuNo; // Menu item next menu no (0 = none) UBYTE ItemIndex; // Menu item index on level UBYTE Items; // Menu items on level } MENULEVEL; typedef struct { MENULEVEL MenuLevels[MENULEVELS]; // See above UBYTE FileId; // VarsUi.MenuFiles[VarsUi.MenuFileLevel].FileId UBYTE MenuLevel; // VarsUi.MenuFiles[VarsUi.MenuFileLevel].MenuLevel } MENUFILE; typedef struct { UBYTE CheckByte; // Check byte (CHECKBYTE) UBYTE DatalogEnabled; // Datalog enabled flag (0 = no) UBYTE VolumeStep; // Volume step (0 - MAX_VOLUME) UBYTE PowerdownCode; // Power down code UWORD DatalogNumber; // Datalog file number (0 - MAX_DATALOGS) } NVDATA; typedef struct { UBYTE StatusText[STATUSTEXT_SIZE + 1]; // RCX name UBYTE Initialized; // Ui init done UWORD SleepTimer; // Sleep timer // Menu system MENUFILE MenuFiles[MENUFILELEVELS]; // Menu file array MENUFILE *pMenuFile; // Actual menu file pointer MENULEVEL *pMenuLevel; // Actual menu item on level, pointer MENUITEM *pMenuItem; // Actual menu item in menu flash file UBYTE MenuFileLevel; // Actual menu file level UBYTE Function; // Running function (0 = none) UBYTE Parameter; // Parameter for running function UBYTE SecondTime; // Second time flag UBYTE EnterOnlyCalls; // Enter button only calls UBYTE ExitOnlyCalls; // Exit button only calls UWORD ButtonTimer; // Button repeat timer UWORD ButtonTime; // Button repeat time UBYTE ButtonOld; // Button old state // Update status UWORD UpdateCounter; // Update counter UBYTE Running; // Running pointer UBYTE BatteryToggle; // Battery flash toggle flag UBYTE NewStatusIcons[NO_OF_STATUSICONS]; // New status icons (used to detect changes) // Low battery voltage UBYTE *LowBattSavedBitmap; // Low battery overwritten bitmap placeholder UBYTE LowBatt; // Low battery volatge flag UBYTE LowBattHasOccured; // Low battery voltage has occured UBYTE LowBattSavedState; // Low battery current state placeholder // General used variables UBYTE *MenuIconTextSave; // Menu icon text save UBYTE *pTmp; // General UBYTE pointer ULONG TmpLength; // General filelength (used in filelist) SWORD TmpHandle; // General filehandle (used in filelist) SWORD Timer; // General tmp purpose timer SWORD ReadoutTimer; // General read out timer UBYTE Tmp; // General UBYTE UBYTE FileType; // General file type UBYTE State; // General tmp purpose state UBYTE Pointer; // General tmp purpose pointer UBYTE Counter; // General tmp purpose counter UBYTE Cursor; // General cursor UBYTE SelectedSensor; // General used for selected sensor UBYTE SelectedPort; // General used for selected port UBYTE SensorReset; UBYTE SensorState; // Sensor state (reset, ask, read) SWORD SensorTimer; // Timer used to time sensor states UBYTE NextState; UBYTE SelectedFilename[FILENAME_LENGTH + 1]; // Selected file name UBYTE FilenameBuffer[FILENAME_LENGTH + 1]; // General filename buffer UBYTE SearchFilenameBuffer[FILENAME_LENGTH + 1];// General filename buffer UBYTE DisplayBuffer[DISPLAYLINE_LENGTH + 1]; // General purpose display buffer UBYTE PortBitmapLeft[SIZE_OF_PORTBITMAP]; // Port no bitmap for left icon UBYTE PortBitmapCenter[SIZE_OF_PORTBITMAP]; // Port no bitmap for center icon UBYTE PortBitmapRight[SIZE_OF_PORTBITMAP]; // Port no bitmap for right icon // Find no of files and find name for file no ULONG FNOFLength; // Length SWORD FNOFHandle; // Handle UBYTE FNOFState; // State UBYTE FNOFSearchBuffer[FILENAME_LENGTH + 1]; // Search buffer UBYTE FNOFNameBuffer[FILENAME_LENGTH + 1]; // File name buffer UBYTE FNOFFileNo; // File no // File list UBYTE FileCenter; // File center UBYTE FileLeft; // File left UBYTE FileRight; // File right UBYTE NoOfFiles; // No of files // On brick programming menu UBYTE ProgramSteps[ON_BRICK_PROGRAMSTEPS]; // On brick programming steps UBYTE ProgramStepPointer; // On brick programming step pointer UBYTE CursorTmp[SIZE_OF_CURSOR]; // On brick programming cursor UBYTE FileHeader[FILEHEADER_LENGTH]; // File header for programs UBYTE *FeedBackText; // Program end text UWORD OBPTimer; // Graphic update timer // BT search menu UBYTE NoOfDevices; // BT search no of devices found UBYTE NoOfNames; // BT search no of names found UBYTE SelectedDevice; // BT selected device UBYTE SelectedSlot; // BT selected slot // BT device list menu UBYTE DevicesKnown; // BT device known flag UBYTE Devices; // BT devices UBYTE DeviceLeft; // BT device left UBYTE DeviceCenter; // BT device center UBYTE DeviceRight; // BT device right UBYTE DeviceType; // BT device type // BT connect Menu UBYTE Slots; // BT connect no of slots UBYTE SlotLeft; // BT connect UBYTE SlotCenter; // BT connect UBYTE SlotRight; // BT connect // Get user string UBYTE GUSTmp; // Seperat tmp for "Get user string" UBYTE GUSState; // Seperat state for "Get user string" UBYTE GUSNoname; // No user entry UBYTE UserString[DISPLAYLINE_LENGTH + 1]; // User string UBYTE DisplayText[DISPLAYLINE_LENGTH + 1]; // Display buffer SBYTE FigurePointer; // Figure cursor UBYTE GUSCursor; // User string cursor // Connect request ULONG CRPasskey; // Passkey to fake wrong pin code UBYTE CRState; // Seperate state for "Connect request" UBYTE CRTmp; // Seperate tmp for "Connect request" // Run files UBYTE *RunIconSave; // Menu center icon save UWORD RunTimer; // Bitmap change timer UBYTE RunBitmapPointer; // Bitmap pointer // Delete files UBYTE SelectedType; // Type of selected files for delete // View SLONG ViewSampleValue; // Latch for sensor values UBYTE ViewSampleValid; // Latch for sensor valid // Datalog ULONG DatalogOldTick; ULONG DatalogRTC; // Real time in mS ULONG DatalogTimer; // Logging main timer ULONG DatalogSampleTime; // Logging sample time ULONG DatalogSampleTimer; // Logging sample timer SLONG DatalogSampleValue[DATALOGPORTS]; // Latch for sensor values UBYTE DatalogSampleValid[DATALOGPORTS]; // Latch for sensor valid UWORD DatalogError; // Error code UBYTE DatalogPort[DATALOGPORTS]; // Logging sensor UBYTE Update; // Update icons flag // NV storage ULONG NVTmpLength; // Non volatile filelength SWORD NVTmpHandle; // Non volatile filehandle UBYTE NVFilename[FILENAME_LENGTH + 1]; // Non volatile file name NVDATA NVData; // Non volatile data // Feedback UBYTE *FBText; // Seperate text pointer for feedback UWORD FBTimer; // Seperate timer for feedback UBYTE FBState; // Seperate state for feedback UBYTE FBPointer; // Seperate pointer for feedback // BT command UBYTE BTIndex; // List index UBYTE BTTmpIndex; // Tmp list index UBYTE BTCommand; // Last lached BT command UBYTE BTPar1; // Last lached BT command parameter 1 UBYTE BTPar2; // Last lached BT command parameter 2 UWORD BTResult; // Last lached BT command result // Error display UBYTE ErrorTimer; // Error show timer UBYTE ErrorFunction; // Error latched function UBYTE ErrorParameter; // Error latched parameter UBYTE ErrorState; // Error latched state UBYTE ErrorString[DISPLAYLINE_LENGTH + 1]; // Error string }VARSUI; void cUiInit(void* pHeader); // Init controller void cUiCtrl(void); // Run controller void cUiExit(void); // Exit controller extern const HEADER cUi; #endif nxt-firmware-1.29.7/src/c_ui.iom000066400000000000000000000152571466344546000164770ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 10-06-08 9:26 $ // // Filename $Workfile:: c_ui.iom $ // // Version $Revision:: 4 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/c_ui.i $ // // Platform C // #ifndef CUI_IOM #define CUI_IOM #define pMapUi ((IOMAPUI*)(pHeaders[ENTRY_UI]->pIOMap)) enum { DEVICETYPE_UNKNOWN, DEVICETYPE_NXT, DEVICETYPE_PHONE, DEVICETYPE_PC }; // Various filenames without extension #define UI_NONVOLATILE "NVConfig" // Ui non volatile config filename #define UI_PROGRAM_DEFAULT "Untitled" // On brick programming default filename #define UI_PROGRAM_TEMP "Program" // On brick programming tmp filename #define UI_PROGRAM_READER "RPGReader" // On brick programming script reader filename #define UI_DATALOG_FILENAME "OBD_" // On brick datalog filename #define UI_DATALOG_DEFAULT "Untitled" // On brick datalog default name #define UI_DATALOG_TEMP "Tmp" // On brick datalog tmp filename #define UI_STARTUP_SOUND "! Startup" // Sound file activated when the menu system starts up #define UI_KEYCLICK_SOUND "! Click" // Sound file activated when key pressed in the menu system #define UI_ATTENTION_SOUND "! Attention" // Sound file activated when incomming BT requests attention // Various text strings #define UI_NAME_DEFAULT "NXT" // Default blue tooth name #define UI_PINCODE_DEFAULT "1234" // Default blue tooth pin code #define UI_PINCODE_NONE_OUT "????" // Fake pin code to deney outgoing request #define UI_PINCODE_NONE_IN "????" // Fake pin code to deney incomming request // Constants related to Flags enum { UI_UPDATE = 0x01, // W - Make changes take effect UI_DISABLE_LEFT_RIGHT_ENTER = 0x02, // RW - Disable left, right and enter button UI_DISABLE_EXIT = 0x04, // RW - Disable exit button UI_REDRAW_STATUS = 0x08, // W - Redraw entire status line UI_RESET_SLEEP_TIMER = 0x10, // W - Reset sleep timeout timer UI_EXECUTE_LMS_FILE = 0x20, // W - Execute LMS file in "LMSfilename" (Try It) UI_BUSY = 0x40, // R - UI busy running or datalogging (popup disabled) UI_ENABLE_STATUS_UPDATE = 0x80 // W - Enable status line to be updated }; // Constants related to State enum { INIT_DISPLAY, // RW - Init display and load font, menu etc. INIT_LOW_BATTERY, // R - Low battery voltage at power on INIT_INTRO, // R - Display intro INIT_WAIT, // RW - Wait for initialization end INIT_MENU, // RW - Init menu system NEXT_MENU, // RW - Next menu icons ready for drawing DRAW_MENU, // RW - Execute function and draw menu icons TEST_BUTTONS, // RW - Wait for buttons to be pressed LEFT_PRESSED, // RW - Load selected function and next menu id RIGHT_PRESSED, // RW - Load selected function and next menu id ENTER_PRESSED, // RW - Load selected function and next menu id EXIT_PRESSED, // RW - Load selected function and next menu id CONNECT_REQUEST, // RW - Request for connection accept EXECUTE_FILE, // RW - Execute file in "LMSfilename" EXECUTING_FILE, // R - Executing file in "LMSfilename" LOW_BATTERY, // R - Low battery at runtime BT_ERROR // R - BT error }; // Constants related to Button enum { BUTTON_NONE, // R - Button inserted are executed BUTTON_LEFT, // W - Insert left arrow button BUTTON_ENTER, // W - Insert enter button BUTTON_RIGHT, // W - Insert right arrow button BUTTON_EXIT // W - Insert exit button }; // Constants related to BlueToothState enum { BT_STATE_VISIBLE = 0x01, // RW - BT visible BT_STATE_CONNECTED = 0x02, // RW - BT connected to something BT_STATE_OFF = 0x04, // RW - BT power off BT_ERROR_ATTENTION = 0x08, // W - BT error attention BT_CONNECT_REQUEST = 0x40, // RW - BT get connect accept in progress BT_PIN_REQUEST = 0x80 // RW - BT get pin code }; typedef struct { MENU *pMenu; // W - Pointer to menu file UWORD BatteryVoltage; // R - Battery voltage in millivolts UBYTE LMSfilename[FILENAME_LENGTH + 1]; // W - LMS filename to execute (Try It) UBYTE Flags; // RW - Update command flags (flags enumerated above) UBYTE State; // RW - UI state (states enumerated above) UBYTE Button; // RW - Insert button (buttons enumerated above) UBYTE RunState; // W - VM Run state (0 = stopped, 1 = running) UBYTE BatteryState; // W - Battery state (0..4 capacity) UBYTE BluetoothState; // W - Bluetooth state (0=on, 1=visible, 2=conn, 3=conn.visible, 4=off, 5=dfu) UBYTE UsbState; // W - Usb state (0=disconnected, 1=connected, 2=working) UBYTE SleepTimeout; // RW - Sleep timeout time (min) UBYTE SleepTimer; // RW - Sleep timer (min) UBYTE Rechargeable; // R - Rechargeable battery (0 = no, 1 = yes) UBYTE Volume; // RW - Volume used in UI (0 - 4) UBYTE Error; // W - Error code UBYTE OBPPointer; // W - Actual OBP step (0 - 4) UBYTE ForceOff; // W - Force off (> 0 = off) }IOMAPUI; #endif nxt-firmware-1.29.7/src/config.h000066400000000000000000000002471466344546000164610ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H /* * This file defines compilation options. */ /* Include intro code and images. */ #define CONFIG_INTRO 1 #endif /* CONFIG_H */ nxt-firmware-1.29.7/src/d_bt.c000066400000000000000000000222151466344546000161160ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 24-04-08 14:33 $ // // Filename $Workfile:: d_bt.c $ // // Version $Revision:: 3 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_bt.c $ // // Platform C // #include "stdconst.h" #include "modules.h" #include "m_sched.h" #include "d_bt.h" #include "d_bt.r" #include enum { BT_FAST_TIMEOUT = 500, BT_CMD_TIMEOUT_2S = 2000, BT_TIMEOUT_30S = 30000 }; #define SETTimeout(TOut) CmdTOut = 0;\ CmdTOutLimit = TOut #define RESETTimeout CmdTOut = 0 static UWORD CmdTOut; static UWORD CmdTOutLimit; void dBtInit(void) { SETTimeout(0); BTInit; BTInitPIOPins; } void dBtSetBcResetPinLow(void) { BTSetResetLow; /* Set Reset pin to Bluecore chip low */ } void dBtSetBcResetPinHigh(void) { BTSetResetHigh; /* Set Reset pin to Bluecore chip high */ } void dBtStartADConverter(void) { BTStartADConverter; } void dBtInitReceive(UBYTE *InputBuffer, UBYTE Mode) { BTInitReceiver(InputBuffer, Mode); } void dBtSetArm7CmdSignal(void) { BT_SetArm7CmdPin; } void dBtClearArm7CmdSignal(void) { BT_ClearArm7CmdPin; } UBYTE dBtGetBc4CmdSignal(void) { UWORD ADValue; BTReadADCValue(ADValue); if (ADValue > 0x200) { ADValue = 1; } else { ADValue = 0; } return(ADValue); } UWORD dBtTxEnd(void) { UWORD TxEnd; REQTxEnd(TxEnd); return(TxEnd); } UWORD dBtCheckForTxBuf(void) { UWORD AvailBytes; AVAILOutBuf(AvailBytes); return(AvailBytes); } void dBtSendMsg(UBYTE *OutputBuffer, UBYTE BytesToSend, UWORD MsgSize) { /* Used for sending a complete message that can be placed in the buffer - */ /* or to send the first part of a message that cannot be placed in the buffer */ /* once (bigger than the buffer) */ BTSendMsg(OutputBuffer,BytesToSend, MsgSize); } void dBtSend(UBYTE *OutputBuffer, UBYTE BytesToSend) { /* Used for continous stream of data to be send */ BTSend(OutputBuffer, BytesToSend); } UWORD dBtReceivedData(UWORD *pLength, UWORD *pBytesToGo) { UWORD RtnVal; RtnVal = TRUE; BTReceivedData(pLength, pBytesToGo); if (*pLength) { SETTimeout(0); } else { if (CmdTOut < CmdTOutLimit) { CmdTOut++; if (CmdTOut >= CmdTOutLimit) { SETTimeout(0); RtnVal = FALSE; } } } return(RtnVal); } void dBtResetTimeOut(void) { RESETTimeout; } void dBtClearTimeOut(void) { SETTimeout(0); } void dBtSendBtCmd(UBYTE Cmd, UBYTE Param1, UBYTE Param2, UBYTE *pBdAddr, UBYTE *pName, UBYTE *pCod, UBYTE *pPin) { UBYTE Tmp; UBYTE SendData; UWORD CheckSumTmp; UBYTE BtOutBuf[128]; UBYTE BtOutCnt; SendData = 0; BtOutCnt = 0; switch (Cmd) { case MSG_BEGIN_INQUIRY: { BtOutBuf[BtOutCnt++] = MSG_BEGIN_INQUIRY; BtOutBuf[BtOutCnt++] = Param1; BtOutBuf[BtOutCnt++] = 0x00; BtOutBuf[BtOutCnt++] = Param2; BtOutBuf[BtOutCnt++] = 0x00; BtOutBuf[BtOutCnt++] = 0x00; BtOutBuf[BtOutCnt++] = 0x00; BtOutBuf[BtOutCnt++] = 0x00; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_CANCEL_INQUIRY: { BtOutBuf[BtOutCnt++] = MSG_CANCEL_INQUIRY; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_CONNECT: { BtOutBuf[BtOutCnt++] = MSG_CONNECT; memcpy(&(BtOutBuf[BtOutCnt]), pBdAddr, SIZE_OF_BDADDR); BtOutCnt += SIZE_OF_BDADDR; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_OPEN_PORT: { BtOutBuf[BtOutCnt++] = MSG_OPEN_PORT; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_LOOKUP_NAME: { BtOutBuf[BtOutCnt++] = MSG_LOOKUP_NAME; memcpy(&(BtOutBuf[BtOutCnt]), pBdAddr, SIZE_OF_BDADDR); BtOutCnt += SIZE_OF_BDADDR; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_ADD_DEVICE: { BtOutBuf[BtOutCnt++] = MSG_ADD_DEVICE; memcpy(&(BtOutBuf[BtOutCnt]), pBdAddr, SIZE_OF_BDADDR); BtOutCnt += SIZE_OF_BDADDR; memcpy(&(BtOutBuf[BtOutCnt]), pName, SIZE_OF_BT_NAME); BtOutCnt += SIZE_OF_BT_NAME; memcpy(&(BtOutBuf[BtOutCnt]), pCod, SIZE_OF_CLASS_OF_DEVICE); BtOutCnt += SIZE_OF_CLASS_OF_DEVICE; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_REMOVE_DEVICE: { BtOutBuf[BtOutCnt++] = MSG_REMOVE_DEVICE; memcpy(&(BtOutBuf[BtOutCnt]), pBdAddr, SIZE_OF_BDADDR); BtOutCnt += SIZE_OF_BDADDR; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_DUMP_LIST: { BtOutBuf[BtOutCnt++] = MSG_DUMP_LIST; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_CLOSE_CONNECTION: { BtOutBuf[BtOutCnt++] = MSG_CLOSE_CONNECTION; BtOutBuf[BtOutCnt++] = Param1; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_ACCEPT_CONNECTION: { BtOutBuf[BtOutCnt++] = MSG_ACCEPT_CONNECTION; BtOutBuf[BtOutCnt++] = Param1; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_PIN_CODE: { BtOutBuf[BtOutCnt++] = MSG_PIN_CODE; memcpy(&(BtOutBuf[BtOutCnt]), pBdAddr, SIZE_OF_BDADDR); BtOutCnt += SIZE_OF_BDADDR; memcpy(&(BtOutBuf[BtOutCnt]), pPin, SIZE_OF_BT_PINCODE); BtOutCnt += SIZE_OF_BT_PINCODE; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_OPEN_STREAM: { BtOutBuf[BtOutCnt++] = MSG_OPEN_STREAM; BtOutBuf[BtOutCnt++] = Param1; SendData = 1; SETTimeout(BT_TIMEOUT_30S); } break; case MSG_START_HEART: { BtOutBuf[BtOutCnt++] = MSG_START_HEART; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_SET_DISCOVERABLE: { BtOutBuf[BtOutCnt++] = MSG_SET_DISCOVERABLE; BtOutBuf[BtOutCnt++] = Param1; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_CLOSE_PORT: { BtOutBuf[BtOutCnt++] = MSG_CLOSE_PORT; BtOutBuf[BtOutCnt++] = 0x03; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_SET_FRIENDLY_NAME: { BtOutBuf[BtOutCnt++] = MSG_SET_FRIENDLY_NAME; memcpy(&(BtOutBuf[BtOutCnt]), pName, SIZE_OF_BT_NAME); BtOutCnt += SIZE_OF_BT_NAME; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_GET_LINK_QUALITY: { BtOutBuf[BtOutCnt++] = MSG_GET_LINK_QUALITY; BtOutBuf[BtOutCnt++] = Param1; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_SET_FACTORY_SETTINGS: { BtOutBuf[BtOutCnt++] = MSG_SET_FACTORY_SETTINGS; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_GET_LOCAL_ADDR: { BtOutBuf[BtOutCnt++] = MSG_GET_LOCAL_ADDR; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_GET_FRIENDLY_NAME: { BtOutBuf[BtOutCnt++] = MSG_GET_FRIENDLY_NAME; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_GET_DISCOVERABLE: { BtOutBuf[BtOutCnt++] = MSG_GET_DISCOVERABLE; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_GET_PORT_OPEN: { BtOutBuf[BtOutCnt++] = MSG_GET_PORT_OPEN; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_GET_VERSION: { BtOutBuf[BtOutCnt++] = MSG_GET_VERSION; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_GET_BRICK_STATUSBYTE: { BtOutBuf[BtOutCnt++] = MSG_GET_BRICK_STATUSBYTE; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; case MSG_SET_BRICK_STATUSBYTE: { BtOutBuf[BtOutCnt++] = MSG_SET_BRICK_STATUSBYTE; BtOutBuf[BtOutCnt++] = Param1; BtOutBuf[BtOutCnt++] = Param2; SendData = 1; SETTimeout(BT_FAST_TIMEOUT); } break; } if (SendData == 1) { CheckSumTmp = 0; for(Tmp = 0; Tmp < BtOutCnt; Tmp++) { CheckSumTmp += BtOutBuf[Tmp]; } CheckSumTmp = (UWORD) (1 + (0xFFFF - CheckSumTmp)); BtOutBuf[BtOutCnt++] = (UBYTE)((CheckSumTmp & 0xFF00)>>8); BtOutBuf[BtOutCnt++] = (UBYTE)(CheckSumTmp & 0x00FF); BTSendMsg(BtOutBuf, BtOutCnt, (UWORD)BtOutCnt); } } void dBtExit(void) { BTExit; } nxt-firmware-1.29.7/src/d_bt.h000066400000000000000000000024271466344546000161260ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_bt.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_bt.h $ // // Platform C // #ifndef D_BT #define D_BT #define STREAM_MODE 1 #define CMD_MODE 2 void dBtInit(void); void dBtExit(void); void dBtStartADConverter(void); void dBtSetArm7CmdSignal(void); void dBtClearArm7CmdSignal(void); void dBtInitReceive(UBYTE *InputBuffer, UBYTE Mode); void dBtSetBcResetPinLow(void); void dBtSetBcResetPinHigh(void); void dBtSendBtCmd(UBYTE Cmd, UBYTE Param1, UBYTE Param2, UBYTE *pBdAddr, UBYTE *pName, UBYTE *pCod, UBYTE *pPin); void dBtSendMsg(UBYTE *pData, UBYTE Length, UWORD MsgSize); void dBtSend(UBYTE *pData, UBYTE Length); void dBtResetTimeOut(void); void dBtClearTimeOut(void); UBYTE dBtGetBc4CmdSignal(void); UWORD dBtTxEnd(void); UWORD dBtReceivedData(UWORD *pLength, UWORD *pBytesToGo); UWORD dBtCheckForTxBuf(void); #endif nxt-firmware-1.29.7/src/d_bt.r000066400000000000000000000515751466344546000161500ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 24-04-08 14:33 $ // // Filename $Workfile:: d_bt.r $ // // Version $Revision:: 3 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_bt.r $ // // Platform C // #ifdef SAM7S256 #if defined (PROTOTYPE_PCB_3) || (PROTOTYPE_PCB_4) #define BT_RX_PIN AT91C_PIO_PA21 #define BT_TX_PIN AT91C_PIO_PA22 #define BT_SCK_PIN AT91C_PIO_PA23 #define BT_RTS_PIN AT91C_PIO_PA24 #define BT_CTS_PIN AT91C_PIO_PA25 #define BT_CS_PIN AT91C_PIO_PA31 #define BT_RST_PIN AT91C_PIO_PA11 #define BT_ARM7_CMD_PIN AT91C_PIO_PA27 #else #endif #define BAUD_RATE 460800L #define SIZE_OF_INBUF 128 #define NO_OF_INBUFFERS 2 #define NO_OF_DMA_OUTBUFFERS 2 #define SIZE_OF_OUTBUF 256 #define PER_ID7_UART_1 0x80 #define UART1_INQ 0x80 #define EXT_LEN_MSG_BIT 0x80 static UBYTE InBuf[NO_OF_INBUFFERS][SIZE_OF_INBUF]; static ULONG InBufPtrs[NO_OF_INBUFFERS]; static UBYTE InBufInPtr; static UBYTE LengthSize; static UBYTE OutDma[NO_OF_DMA_OUTBUFFERS][SIZE_OF_OUTBUF]; static UBYTE DmaBufPtr; static UBYTE *pBuffer; static UBYTE MsgIn; static UBYTE InBufOutCnt; static UWORD FullRxLength; static UWORD RemainingLength; #define ENABLEDebugOutput {\ *AT91C_PIOA_PER = 0x20000000; /* Enable PIO on PA029 */\ *AT91C_PIOA_OER = 0x20000000; /* PA029 set to Output */\ } #define SETDebugOutputHigh *AT91C_PIOA_SODR = 0x20000000 #define SETDebugOutputLow *AT91C_PIOA_CODR = 0x20000000 #define BTInit {\ UBYTE Tmp;\ LengthSize = 1;\ InBufInPtr = 0;\ for(Tmp = 0; Tmp < NO_OF_INBUFFERS; Tmp++)\ {\ InBufPtrs[Tmp] = (ULONG)&(InBuf[Tmp][0]);\ }\ *AT91C_PMC_PCER = PER_ID7_UART_1; /* Enable PMC clock for UART 1 */\ *AT91C_PIOA_PDR = BT_RX_PIN | BT_TX_PIN | BT_SCK_PIN | BT_RTS_PIN | BT_CTS_PIN; /* Disable Per. A on PA21, PA22, PA23, PA24 & PA25 */\ *AT91C_PIOA_ASR = BT_RX_PIN | BT_TX_PIN | BT_SCK_PIN | BT_RTS_PIN | BT_CTS_PIN; /* Enable Per. A on PA21, PA22, PA23, PA24 & PA25 */\ *AT91C_US1_CR = AT91C_US_RSTSTA; /* Resets pins on UART1 */\ *AT91C_US1_CR = AT91C_US_STTTO; /* Start timeout functionality after 1 byte */\ *AT91C_US1_RTOR = 10000; /* Approxitely 20 mS,x times bit time with 115200 bit pr s */\ *AT91C_US1_IDR = AT91C_US_TIMEOUT; /* Disable interrupt on timeout */\ *AT91C_AIC_IDCR = UART1_INQ; /* Disable UART1 interrupt */\ *AT91C_AIC_ICCR = UART1_INQ; /* Clear interrupt register */\ *AT91C_US1_MR = AT91C_US_USMODE_HWHSH; /* Set UART with HW handshake */\ *AT91C_US1_MR &= ~AT91C_US_SYNC; /* Set UART in asynchronous mode */\ *AT91C_US1_MR |= AT91C_US_CLKS_CLOCK; /* Clock setup MCK*/\ *AT91C_US1_MR |= AT91C_US_CHRL_8_BITS; /* UART using 8-bit */\ *AT91C_US1_MR |= AT91C_US_PAR_NONE; /* UART using none parity bit */\ *AT91C_US1_MR |= AT91C_US_NBSTOP_1_BIT; /* UART using 1 stop bit */\ *AT91C_US1_MR |= AT91C_US_OVER; /* UART is using 8-bit sampling */\ *AT91C_US1_BRGR = ((OSC/8/BAUD_RATE) | (((OSC/8) - ((OSC/8/BAUD_RATE) * BAUD_RATE)) / ((BAUD_RATE + 4)/8)) << 16);\ *AT91C_US1_PTCR = (AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); /* Disable of TX & RX with DMA */\ *AT91C_US1_RCR = 0; /* Receive Counter Register */\ *AT91C_US1_TCR = 0; /* Transmit Counter Register */\ *AT91C_US1_RNPR = 0;\ *AT91C_US1_TNPR = 0;\ Tmp = *AT91C_US1_RHR;\ Tmp = *AT91C_US1_CSR;\ *AT91C_US1_RPR = (unsigned int)&(InBuf[InBufInPtr][0]); /* Initialise receiver buffer using DMA */\ *AT91C_US1_RCR = SIZE_OF_INBUF;\ *AT91C_US1_RNPR = (unsigned int)&(InBuf[(InBufInPtr + 1)%NO_OF_INBUFFERS][0]);\ *AT91C_US1_RNCR = SIZE_OF_INBUF;\ MsgIn = 0;\ InBufOutCnt = 0;\ FullRxLength = 0;\ RemainingLength = 0;\ *AT91C_US1_CR = AT91C_US_RXEN | AT91C_US_TXEN; /* Enable Tx & Rx on UART 1*/\ *AT91C_US1_PTCR = (AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); /* Enable of TX & RX with DMA */\ } #define BTInitPIOPins {\ *AT91C_PIOA_PER = BT_CS_PIN | BT_RST_PIN; /* Enable PIO on PA11 & PA31 */\ *AT91C_PIOA_OER = BT_CS_PIN | BT_RST_PIN; /* PA11 & PA31 set to output */\ *AT91C_PIOA_SODR = BT_CS_PIN | BT_RST_PIN; /* PA31 & PA11 set output high */\ *AT91C_PIOA_PPUDR = BT_ARM7_CMD_PIN; /* Disable PULL-UP resistor on PA27 */\ *AT91C_PIOA_PER = BT_ARM7_CMD_PIN; /* Enable PIO on PA27 */\ *AT91C_PIOA_CODR = BT_ARM7_CMD_PIN; /* PA27 set output low */\ *AT91C_PIOA_OER = BT_ARM7_CMD_PIN; /* PA27 set to output */\ } #define BTStartADConverter {\ *AT91C_ADC_CHER = AT91C_ADC_CH6 | AT91C_ADC_CH4; \ ADStart; \ while(!((*AT91C_ADC_SR) & AT91C_ADC_CH6)); \ *AT91C_ADC_CHDR = AT91C_ADC_CH6 | AT91C_ADC_CH4; \ } #define BTReadADCValue(ADValue) ADValue = *AT91C_ADC_CDR6; #define BTSetResetHigh {\ *AT91C_PIOA_SODR = BT_RST_PIN; /* PA11 set output high */\ } #define BTSetResetLow {\ *AT91C_PIOA_CODR = BT_RST_PIN; /* PA11 set output low */\ } #define BTInitReceiver(InputBuffer, Mode)\ {\ pBuffer = InputBuffer;\ MsgIn = 0;\ FullRxLength = 0;\ if (STREAM_MODE == Mode)\ {\ LengthSize = 2;\ }\ else\ {\ LengthSize = 1;\ }\ } #define BT_SetArm7CmdPin *AT91C_PIOA_SODR = BT_ARM7_CMD_PIN #define BT_ClearArm7CmdPin *AT91C_PIOA_CODR = BT_ARM7_CMD_PIN #define BT_GetBc4CmdPin *AT91C_PIOA_PDSR & BT_BC4_CMD_PIN #define REQTxEnd(TxEnd) TxEnd = FALSE;\ if ((!(*AT91C_US1_TNCR)) && (!(*AT91C_US1_TCR)))\ {\ TxEnd = TRUE;\ } #define AVAILOutBuf(Avail) if (!(*AT91C_US1_TNCR))\ {\ Avail = SIZE_OF_OUTBUF;\ }\ else\ {\ Avail = 0;\ } #define BTSend(OutputBuffer, BytesToSend)\ {\ UWORD Avail;\ AVAILOutBuf(Avail);\ if (BytesToSend < (Avail - 1))\ {\ memcpy(&(OutDma[DmaBufPtr][0]), OutputBuffer, BytesToSend);\ *AT91C_US1_TNPR = (unsigned int)&(OutDma[DmaBufPtr][0]);\ *AT91C_US1_TNCR = BytesToSend;\ DmaBufPtr = (DmaBufPtr + 1) % NO_OF_DMA_OUTBUFFERS;\ }\ } #define BTSendMsg(OutputBuffer, BytesToSend, MsgSize)\ {\ UWORD Avail;\ AVAILOutBuf(Avail);\ if (BytesToSend < (Avail - 1))\ {\ if (2 == LengthSize)\ {\ OutDma[DmaBufPtr][0] = (UBYTE)MsgSize;\ OutDma[DmaBufPtr][1] = (UBYTE)(MsgSize>>8);\ }\ else\ {\ OutDma[DmaBufPtr][0] = (UBYTE)MsgSize;\ }\ memcpy(&(OutDma[DmaBufPtr][LengthSize]), OutputBuffer, BytesToSend);\ *AT91C_US1_TNPR = (unsigned int)&(OutDma[DmaBufPtr][0]);\ *AT91C_US1_TNCR = BytesToSend + LengthSize;\ DmaBufPtr = (DmaBufPtr + 1) % NO_OF_DMA_OUTBUFFERS;\ }\ } #define BTReceivedData(pByteCnt, pToGo)\ {\ UWORD InCnt, Cnt;\ *pByteCnt = 0;\ *pToGo = 0;\ InCnt = (SIZE_OF_INBUF - *AT91C_US1_RCR);\ if (*AT91C_US1_RNCR == 0)\ {\ InCnt = SIZE_OF_INBUF;\ }\ InCnt -= InBufOutCnt; /* Remove already read bytes */\ if (InCnt)\ {\ if (0 == FullRxLength) /* FullRxLength still to be calculated */\ {\ while((MsgIn < LengthSize) && (InCnt > 0))\ {\ pBuffer[MsgIn] = InBuf[InBufInPtr][InBufOutCnt];\ MsgIn++;\ InBufOutCnt++;\ InCnt--;\ }\ if (LengthSize == MsgIn)\ {\ if (2 == LengthSize)\ {\ FullRxLength = pBuffer[1];\ FullRxLength <<= 8;\ FullRxLength |= pBuffer[0];\ /* Remove Length when in strean mode */\ MsgIn = 0;\ }\ else\ {\ FullRxLength = pBuffer[0];\ }\ RemainingLength = FullRxLength;\ }\ else\ {\ /* Length still not received */\ FullRxLength = 0;\ }\ }\ if (FullRxLength)\ {\ /* Incomming msg in progress */\ /* room for bytes? */\ if (InCnt >= RemainingLength)\ {\ /* Remaining msg bytes are in the buffer */\ /* Can remaining byte be stored in buffer? */\ if ((MsgIn + RemainingLength) <= SIZE_OF_INBUF)\ {\ /* All bytes can be stored */\ for (Cnt = 0; Cnt < RemainingLength; Cnt++)\ {\ pBuffer[MsgIn] = InBuf[InBufInPtr][InBufOutCnt];\ MsgIn++;\ InBufOutCnt++;\ }\ *pByteCnt = MsgIn;\ *pToGo = 0;\ FullRxLength = 0;\ RemainingLength = 0;\ MsgIn = 0;\ }\ else\ {\ for (Cnt = 0; MsgIn < SIZE_OF_INBUF; Cnt++)\ {\ pBuffer[MsgIn] = InBuf[InBufInPtr][InBufOutCnt];\ MsgIn++;\ InBufOutCnt++;\ }\ *pByteCnt = SIZE_OF_INBUF;\ RemainingLength -= Cnt;\ *pToGo = RemainingLength;\ MsgIn = 0;\ }\ }\ else\ {\ if ((InCnt + MsgIn) < SIZE_OF_INBUF)\ {\ /* Received bytes do not fill up the buffer */\ for (Cnt = 0; Cnt < InCnt; Cnt++)\ {\ pBuffer[MsgIn] = InBuf[InBufInPtr][InBufOutCnt];\ MsgIn++;\ InBufOutCnt++;\ }\ RemainingLength -= InCnt;\ }\ else\ {\ /* Received bytes fill up the buffer */\ for (Cnt = 0; MsgIn < SIZE_OF_INBUF; Cnt++)\ {\ pBuffer[MsgIn] = InBuf[InBufInPtr][InBufOutCnt];\ MsgIn++;\ InBufOutCnt++;\ }\ *pByteCnt = SIZE_OF_INBUF;\ RemainingLength -= Cnt; /* Only substract no removed */\ *pToGo = RemainingLength;\ MsgIn = 0;\ }\ }\ }\ }\ if ((*AT91C_US1_RNCR == 0) && (SIZE_OF_INBUF == InBufOutCnt))\ {\ InBufOutCnt = 0;\ *AT91C_US1_RNPR = (unsigned int)InBufPtrs[InBufInPtr];\ *AT91C_US1_RNCR = SIZE_OF_INBUF;\ InBufInPtr = (InBufInPtr + 1) % NO_OF_INBUFFERS;\ }\ } #define BTExit {\ *AT91C_PMC_PCDR = PER_ID7_UART_1; /* Disable PMC clock for UART 1*/\ *AT91C_US1_IDR = AT91C_US_TIMEOUT; /* Disable interrupt on timeout */\ *AT91C_AIC_IDCR = UART1_INQ; /* Disable PIO interrupt */\ *AT91C_AIC_ICCR = UART1_INQ; /* Clear interrupt register */\ } #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/d_button.c000066400000000000000000000013421466344546000170220ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_button.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_butt $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_button.h" #include "d_button.r" static UBYTE TimeTick; void dButtonInit(UBYTE Prescaler) { TimeTick = Prescaler; BUTTONInit; } void dButtonRead(UBYTE *pButton) { BUTTONRead(pButton); } void dButtonExit(void) { BUTTONExit; } nxt-firmware-1.29.7/src/d_button.h000066400000000000000000000010721466344546000170270ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_button.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_butt $ // // Platform C // #ifndef D_BUTTON #define D_BUTTON void dButtonInit(UBYTE Prescaler); void dButtonExit(void); void dButtonRead(UBYTE *pButton); #endif nxt-firmware-1.29.7/src/d_button.r000066400000000000000000000230551466344546000170460ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_button.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_butt $ // // Platform C // #ifdef SAM7S256 static UBYTE PrellCnt[NOS_OF_AVR_BTNS]; static UWORD OldVal; static UBYTE OldState; static UBYTE RisingTime; #define PRELL_TIME (60/TimeTick) #define RISING_THRESHOLD (10/TimeTick) #define BUTTONInit {\ UBYTE Tmp;\ for (Tmp = 0; Tmp < NOS_OF_AVR_BTNS; Tmp++)\ {\ PrellCnt[Tmp] = 0;\ }\ IoFromAvr.Buttons = 0;\ OldVal = 0;\ OldState = 0;\ RisingTime = 0;\ } #if defined (PROTOTYPE_PCB_3) || (PROTOTYPE_PCB_4) /* Buttons read here are free of prell or jitter */ /* And because it's an AD value returned from the AVR */ /* then a peak detector is needed */ #define BUTTONRead(pB) {\ UBYTE Tmp, BtnPtr;\ UWORD TmpBtn;\ *pB = OldState;\ BtnPtr = 0x01;\ if (OldVal < IoFromAvr.Buttons)\ {\ OldVal = IoFromAvr.Buttons;\ RisingTime = 0;\ }\ else\ {\ if (OldVal > (IoFromAvr.Buttons + 20))\ {\ OldVal = IoFromAvr.Buttons;\ RisingTime = 0;\ }\ else\ {\ if (RisingTime > RISING_THRESHOLD)\ {\ TmpBtn = IoFromAvr.Buttons;\ if (0x40 > TmpBtn)\ {\ TmpBtn = 0x00;\ }\ else if (0x100 > TmpBtn)\ {\ TmpBtn = 0x04;\ }\ else if (0x1FF > TmpBtn)\ {\ TmpBtn = 0x02;\ }\ else if (0x5FF > TmpBtn)\ {\ TmpBtn = 0x01;\ }\ else\ {\ TmpBtn = 0x08;\ }\ for (Tmp = 0; Tmp < NOS_OF_AVR_BTNS; Tmp++)\ {\ if ((TmpBtn) & BtnPtr)\ {\ *pB |= BtnPtr;\ PrellCnt[Tmp] = PRELL_TIME;\ }\ else\ {\ /* btn not pressed */\ if (0 != PrellCnt[Tmp])\ {\ PrellCnt[Tmp]--;\ }\ else\ {\ *pB &= ~BtnPtr;\ }\ }\ BtnPtr <<= 1;\ }\ OldState = *pB;\ }\ else\ {\ RisingTime++;\ }\ }\ }\ } #else // Buttons read here are free of prell or jitter #define BUTTONRead(pB) {\ UBYTE Tmp, BtnPtr;\ UWORD TmpBtn;\ *pB = OldState;\ BtnPtr = 0x01;\ if ((OldVal) < IoFromAvr.Buttons)\ {\ OldVal = IoFromAvr.Buttons;\ }\ else\ {\ if ((OldVal) > IoFromAvr.Buttons)\ {\ OldVal = IoFromAvr.Buttons;\ }\ else\ {\ TmpBtn = IoFromAvr.Buttons;\ if (100 > TmpBtn)\ {\ TmpBtn = 0x00;\ }\ else if (170 > TmpBtn)\ {\ TmpBtn = 0x01;\ }\ else if (255 > TmpBtn)\ {\ TmpBtn = 0x02;\ }\ else if (1000 > TmpBtn)\ {\ TmpBtn = 0x04;\ }\ else if (1024 > TmpBtn)\ {\ TmpBtn = 0x08;\ }\ for (Tmp = 0; Tmp < NOS_OF_AVR_BTNS; Tmp++)\ {\ if ((TmpBtn) & BtnPtr)\ {\ *pB |= BtnPtr;\ PrellCnt[Tmp] = PRELL_TIME;\ }\ else\ {\ /* btn not pressed */\ if (0 != PrellCnt[Tmp])\ {\ PrellCnt[Tmp]--;\ }\ else\ {\ *pB &= ~BtnPtr;\ }\ }\ BtnPtr <<= 1;\ }\ OldState = *pB;\ }\ }\ } #endif #define BUTTONExit #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/d_display.c000066400000000000000000000016711466344546000171610ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_display.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_disp $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_display.h" #include "d_display.r" void dDisplayInit(void) { DISPLAYInit; } void dDisplayOn(UBYTE On) { if (On) { DISPLAYOn; } else { DISPLAYOff; } } UBYTE dDisplayUpdate(UWORD Height,UWORD Width,UBYTE *pImage) { return (DISPLAYUpdate(Height,Width,pImage)); } void dDisplayExit(void) { DISPLAYExit; } nxt-firmware-1.29.7/src/d_display.h000066400000000000000000000015351466344546000171650ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_display.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_disp $ // // Platform C // #ifndef D_DISPLAY #define D_DISPLAY void dDisplayInit(void); void dDisplayOn(UBYTE On); UBYTE dDisplayUpdate(UWORD Height,UWORD Width,UBYTE *pImage); void dDisplayExit(void); typedef struct { UBYTE StartX; UBYTE StartY; UBYTE PixelsX; UBYTE PixelsY; } SCREEN_CORDINATE; #endif nxt-firmware-1.29.7/src/d_display.r000066400000000000000000000326301466344546000171770ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_display.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_disp $ // // Platform C // #ifdef SAM7S256 // Display 128 x 64 // 1/65 duty, 1/9 bias // VLCD 12.0V // SPI interface // // PCB LCD ARM PIO // ------ ----- ---- ----- // CS_DIS -CS1 PA10 NPCS2 (PB) // DIS_A0 A0 PA12 PA12 // DIS_SCL SCL PA14 SPCK (PA) // DIS_SDA SI PA13 MOSI (PA) // CPOL = 0, NCPHA=0, #define BT_RESET_OUT AT91C_PIO_PA11 #define BT_RESET_IN AT91C_PIO_PA29 #define BT_MOSI_OUT AT91C_PIO_PA13 #define BT_MOSI_IN AT91C_PIO_PA20 #define BT_CLK_OUT AT91C_PIO_PA14 #define BT_CLK_IN AT91C_PIO_PA28 #define BT_CE_OUT AT91C_PIO_PA31 #define BT_CE_IN AT91C_PIO_PA19 #define BT_REA_OUT AT91C_PIO_PA7 #define BT_MISO_OUT AT91C_PIO_PA6 #define BT_MISO_IN AT91C_PIO_PA12 __ramfunc void SpiBtIo(void) { register ULONG Port; *AT91C_AIC_IDCR = 0xFFFFFFFF; /* Disable all interrupts */ *AT91C_PIOA_PER = BT_RESET_OUT; /* Enable pin RESET out */ *AT91C_PIOA_OER = BT_RESET_OUT; /* Set output */ *AT91C_PIOA_SODR = BT_RESET_OUT; /* Set high */ *AT91C_PIOA_PER = BT_MOSI_OUT; /* Enable pin MOSI out */ *AT91C_PIOA_OER = BT_MOSI_OUT; /* Set output */ *AT91C_PIOA_PER = BT_CLK_OUT; /* Enable pin CLK out */ *AT91C_PIOA_OER = BT_CLK_OUT; /* Set output */ *AT91C_PIOA_PER = BT_CE_OUT; /* Enable pin CE out */ *AT91C_PIOA_OER = BT_CE_OUT; /* Set output */ *AT91C_PIOA_PER = BT_REA_OUT; /* Enable pin REA out */ *AT91C_PIOA_OER = BT_REA_OUT; /* Set output */ *AT91C_PIOA_SODR = BT_REA_OUT; /* Set high */ *AT91C_PIOA_PER = BT_MISO_OUT; /* Enable pin MISO out */ *AT91C_PIOA_OER = BT_MISO_OUT; /* Set output */ *AT91C_PIOA_PER = BT_RESET_IN; /* Enable pin RESET in */ *AT91C_PIOA_ODR = BT_RESET_IN; /* Set input */ *AT91C_PIOA_IFDR = BT_RESET_IN; /* Disable filter */ *AT91C_PIOA_IDR = BT_RESET_IN; /* Disable interrupt */ *AT91C_PIOA_MDDR = BT_RESET_IN; /* Disable multidriver */ *AT91C_PIOA_PPUDR = BT_RESET_IN; /* Disable pullup */ *AT91C_PIOA_PER = BT_MOSI_IN; /* Enable pin MOSI in */ *AT91C_PIOA_ODR = BT_MOSI_IN; /* Set input */ *AT91C_PIOA_IFDR = BT_MOSI_IN; /* Disable filter */ *AT91C_PIOA_IDR = BT_MOSI_IN; /* Disable interrupt */ *AT91C_PIOA_MDDR = BT_MOSI_IN; /* Disable multidriver */ *AT91C_PIOA_PPUDR = BT_MOSI_IN; /* Disable pullup */ *AT91C_PIOA_PER = BT_CLK_IN; /* Enable pin CLK in */ *AT91C_PIOA_ODR = BT_CLK_IN; /* Set input */ *AT91C_PIOA_IFDR = BT_CLK_IN; /* Disable filter */ *AT91C_PIOA_IDR = BT_CLK_IN; /* Disable interrupt */ *AT91C_PIOA_MDDR = BT_CLK_IN; /* Disable multidriver */ *AT91C_PIOA_PPUDR = BT_CLK_IN; /* Disable pullup */ *AT91C_PIOA_PER = BT_CE_IN; /* Enable pin CE in */ *AT91C_PIOA_ODR = BT_CE_IN; /* Set input */ *AT91C_PIOA_IFDR = BT_CE_IN; /* Disable filter */ *AT91C_PIOA_IDR = BT_CE_IN; /* Disable interrupt */ *AT91C_PIOA_MDDR = BT_CE_IN; /* Disable multidriver */ *AT91C_PIOA_PPUDR = BT_CE_IN; /* Disable pullup */ *AT91C_PIOA_PER = BT_MISO_IN; /* Enable pin MISO in */ *AT91C_PIOA_ODR = BT_MISO_IN; /* Set input */ *AT91C_PIOA_IFDR = BT_MISO_IN; /* Disable filter */ *AT91C_PIOA_IDR = BT_MISO_IN; /* Disable interrupt */ *AT91C_PIOA_MDDR = BT_MISO_IN; /* Disable multidriver */ *AT91C_PIOA_PPUDR = BT_MISO_IN; /* Disable pullup */ while (1) { Port = *AT91C_PIOA_PDSR; if ((Port & BT_MISO_IN)) { *AT91C_PIOA_SODR = BT_MISO_OUT; } else { *AT91C_PIOA_CODR = BT_MISO_OUT; } if ((Port & BT_MOSI_IN)) { *AT91C_PIOA_SODR = BT_MOSI_OUT; } else { *AT91C_PIOA_CODR = BT_MOSI_OUT; } if ((Port & BT_CLK_IN)) { *AT91C_PIOA_SODR = BT_CLK_OUT; } else { *AT91C_PIOA_CODR = BT_CLK_OUT; } if ((Port & BT_CE_IN)) { *AT91C_PIOA_SODR = BT_CE_OUT; } else { *AT91C_PIOA_CODR = BT_CE_OUT; } } } void BtIo(void) { SpiBtIo(); } #define SPI_BITRATE 2000000 #define SPIA0High {\ *AT91C_PIOA_SODR = AT91C_PIO_PA12;\ } #define SPIA0Low {\ *AT91C_PIOA_CODR = AT91C_PIO_PA12;\ } #define SPIInit {\ *AT91C_PMC_PCER = (1L << AT91C_ID_SPI); /* Enable MCK clock */\ *AT91C_PIOA_PER = AT91C_PIO_PA12; /* Enable A0 on PA12 */\ *AT91C_PIOA_OER = AT91C_PIO_PA12;\ *AT91C_PIOA_CODR = AT91C_PIO_PA12;\ *AT91C_PIOA_PDR = AT91C_PA14_SPCK; /* Enable SPCK on PA14 */\ *AT91C_PIOA_ASR = AT91C_PA14_SPCK;\ *AT91C_PIOA_ODR = AT91C_PA14_SPCK;\ *AT91C_PIOA_OWER = AT91C_PA14_SPCK;\ *AT91C_PIOA_MDDR = AT91C_PA14_SPCK;\ *AT91C_PIOA_PPUDR = AT91C_PA14_SPCK;\ *AT91C_PIOA_IFDR = AT91C_PA14_SPCK;\ *AT91C_PIOA_CODR = AT91C_PA14_SPCK;\ *AT91C_PIOA_IDR = AT91C_PA14_SPCK;\ *AT91C_PIOA_PDR = AT91C_PA13_MOSI; /* Enable mosi on PA13 */\ *AT91C_PIOA_ASR = AT91C_PA13_MOSI;\ *AT91C_PIOA_ODR = AT91C_PA13_MOSI;\ *AT91C_PIOA_OWER = AT91C_PA13_MOSI;\ *AT91C_PIOA_MDDR = AT91C_PA13_MOSI;\ *AT91C_PIOA_PPUDR = AT91C_PA13_MOSI;\ *AT91C_PIOA_IFDR = AT91C_PA13_MOSI;\ *AT91C_PIOA_CODR = AT91C_PA13_MOSI;\ *AT91C_PIOA_IDR = AT91C_PA13_MOSI;\ *AT91C_PIOA_PDR = AT91C_PA10_NPCS2; /* Enable npcs0 on PA11 */\ *AT91C_PIOA_BSR = AT91C_PA10_NPCS2;\ *AT91C_PIOA_ODR = AT91C_PA10_NPCS2;\ *AT91C_PIOA_OWER = AT91C_PA10_NPCS2;\ *AT91C_PIOA_MDDR = AT91C_PA10_NPCS2;\ *AT91C_PIOA_PPUDR = AT91C_PA10_NPCS2;\ *AT91C_PIOA_IFDR = AT91C_PA10_NPCS2;\ *AT91C_PIOA_CODR = AT91C_PA10_NPCS2;\ *AT91C_PIOA_IDR = AT91C_PA10_NPCS2;\ *AT91C_SPI_CR = AT91C_SPI_SWRST; /* Soft reset */\ *AT91C_SPI_CR = AT91C_SPI_SPIEN; /* Enable spi */\ *AT91C_SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | (0xB << 16);\ AT91C_SPI_CSR[2] = ((OSC / SPI_BITRATE) << 8) | AT91C_SPI_CPOL;\ } #define SPIWrite(pString,Length) {\ *AT91C_SPI_TPR = (unsigned int)pString;\ *AT91C_SPI_TCR = (unsigned int)Length;\ *AT91C_SPI_PTCR = AT91C_PDC_TXTEN;\ } #define CMD 0 #define DAT 1 #define DISP_LINES 8 #if defined (PROTOTYPE_PCB_3) || (PROTOTYPE_PCB_4) #define ACTUAL_WIDTH 100 UBYTE DisplayInitString[] = { 0xEB, // LCD bias setting = 1/9 0xEB 0x2F, // Power control = internal 0x2F 0xA4, // All points not on 0xA4 0xA6, // Not inverse 0xA6 0x40, // Start line = 0 0x40 0x81, // Electronic volume 0x81 0x5A, // -"- 0x5F 0xC4, // LCD mapping 0xC4 0x27, // Set temp comp. 0x27- 0x29, // Panel loading 0x28 0-1 0xA0, // Framerate 0xA0- 0x88, // CA++ 0x88- 0x23, // Multiplex 1:65 0x23 0xAF // Display on 0xAF }; #else #define ACTUAL_WIDTH 128 UBYTE DisplayInitString[] = { 0xA2, // LCD bias setting = 1/9 0x2F, // Power control = internal 0xA4, // All points not on 0xA6, // Not inverse 0x40, // Start line = 0 0x81, // Electronic volume 0x3F, // -"- 0xA0, // LCD mapping 0x27, // Resistor ratio 0xC8, // Common output state selection 0xF8, // Booster ratio 0x00, // -"- 0xE3, // nop 0xAF // Display on }; #endif UBYTE DisplayLineString[DISP_LINES][3] = { { 0xB0,0x10,0x00 }, { 0xB1,0x10,0x00 }, { 0xB2,0x10,0x00 }, { 0xB3,0x10,0x00 }, { 0xB4,0x10,0x00 }, { 0xB5,0x10,0x00 }, { 0xB6,0x10,0x00 }, { 0xB7,0x10,0x00 } }; UBYTE DisplayWrite(UBYTE Type,UBYTE *pData,UWORD Length) { UBYTE Result = FALSE; if ((*AT91C_SPI_SR & AT91C_SPI_TXEMPTY)) { if (Type) { SPIA0High; } else { SPIA0Low; } SPIWrite(pData,Length); Result = TRUE; } return (Result); } UBYTE DisplayUpdate(UWORD Height,UWORD Width,UBYTE *pImage) { static UWORD State = 0; static UWORD Line; if (State == 0) { if (DisplayWrite(CMD,(UBYTE*)DisplayInitString,sizeof(DisplayInitString)) == TRUE) { Line = 0; State++; } } else { if ((State & 1)) { if (DisplayWrite(CMD,(UBYTE*)DisplayLineString[Line],3) == TRUE) { State++; } } else { if (DisplayWrite(DAT,(UBYTE*)&pImage[Line * Width],ACTUAL_WIDTH) == TRUE) { State++; if (++Line >= (Height / 8)) { State = 0; } } } } return (State); } #if defined (PROTOTYPE_PCB_3) #define DISPLAYInit {\ TSTInit;\ TSTOn;\ SPIInit;\ } #else #define DISPLAYInit {\ SPIInit;\ } #endif #define DISPLAYOn {\ DisplayInitString[6] = 0x5A;\ DisplayInitString[13] = 0xAF;\ } #define DISPLAYOff {\ DisplayInitString[6] = 0x00;\ DisplayInitString[13] = 0xAE;\ } #define DISPLAYUpdate(H,W,I) DisplayUpdate(H,W,I) #define DISPLAYExit #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/d_hispeed.c000066400000000000000000000017121466344546000171310ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_hispeed.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_hisp $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_hispeed.h" #include "d_hispeed.r" void dHiSpeedInit(void) { HIGHSPEEDInit; } void dHiSpeedSendData(UBYTE *OutputBuffer, UBYTE BytesToSend) { HIGHSPEEDSendDmaData(OutputBuffer,BytesToSend); } void dHiSpeedSetupUart(void) { HIGHSPEEDSetupUart; } void dHiSpeedInitReceive(UBYTE *InputBuffer) { HIGHSPEEDInitReceiver(InputBuffer); } void dHiSpeedReceivedData(UWORD *ByteCnt) { HIGHSPEEDReceivedData(ByteCnt); } void dHiSpeedExit(void) { HIGHSPEEDExit; } nxt-firmware-1.29.7/src/d_hispeed.h000066400000000000000000000013251466344546000171360ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_hispeed.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_hisp $ // // Platform C // #ifndef D_HISPEED #define D_HISPEED void dHiSpeedInit(void); void dHiSpeedSendData(UBYTE *OutputBuffer, UBYTE BytesToSend); void dHiSpeedSetupUart(void); void dHiSpeedInitReceive(UBYTE *InputBuffer); void dHiSpeedReceivedData(UWORD *ByteCnt); void dHiSpeedExit(void); #endif nxt-firmware-1.29.7/src/d_hispeed.r000066400000000000000000000301431466344546000171500ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_hispeed.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_hisp $ // // Platform C // #ifdef SAM7S256 #if defined (PROTOTYPE_PCB_3) || (PROTOTYPE_PCB_4) #define HIGHSPEED_RX_PIN AT91C_PIO_PA5 #define HIGHSPEED_TX_PIN AT91C_PIO_PA6 #define HIGHSPEED_RTS_PIN AT91C_PIO_PA7 #else #endif #define PER_ID6_UART_0 0x40 #define UART0_INQ 0x40 #define BAUD_RATE 921600L #define SIZE_OF_INBUF 128 #define NO_OF_INBUFFERS 2 #define SIZE_OF_OUTBUF 128 #define NO_OF_DMA_OUTBUFFERS 1 static UBYTE InBuf[NO_OF_INBUFFERS][SIZE_OF_INBUF]; static ULONG InBufPtrs[NO_OF_INBUFFERS]; static UBYTE InBufInPtr; static UBYTE OutDma[NO_OF_DMA_OUTBUFFERS][SIZE_OF_OUTBUF]; static UBYTE DmaBufPtr; static UBYTE *pBuffer; static UBYTE MsgIn; static UBYTE InBufOutCnt; #define HIGHSPEEDInit {\ *AT91C_PIOA_PER = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN; /* Enable PIO on PA07, PA06 & PA05 */\ *AT91C_PIOA_PPUDR = HIGHSPEED_RX_PIN | HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN; /* Disable Pull-up resistor */\ *AT91C_PIOA_OER = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN; /* PA07 & PA06 set to Output */\ *AT91C_PIOA_CODR = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN; /* Set output low */\ } #define HIGHSPEEDSetupUart {\ UBYTE Tmp;\ InBufInPtr = 0;\ for(Tmp = 0; Tmp < NO_OF_INBUFFERS; Tmp++)\ {\ InBufPtrs[Tmp] = (ULONG)&(InBuf[Tmp][0]);\ }\ *AT91C_PMC_PCER = PER_ID6_UART_0; /* Enable PMC clock for UART 0 */\ *AT91C_PIOA_PPUDR = HIGHSPEED_RX_PIN | HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN; /* Disable Pull-up resistor */\ *AT91C_PIOA_PDR = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN; /* Disable Per. A on PA5, PA6 & PA7 */\ *AT91C_PIOA_ASR = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN;; /* Enable Per. A on PA5, PA6 & PA7 */\ *AT91C_US0_CR = AT91C_US_RSTSTA; /* Resets pins on UART0 */\ *AT91C_US0_CR = AT91C_US_STTTO; /* Start timeout functionality after 1 byte */\ *AT91C_US0_RTOR = 2400; /* Approxitely 20 mS,x times bit time with 115200 bit pr s */\ *AT91C_US0_IDR = AT91C_US_TIMEOUT; /* Disable interrupt on timeout */\ *AT91C_AIC_IDCR = UART0_INQ; /* Disable UART0 interrupt */\ *AT91C_AIC_ICCR = UART0_INQ; /* Clear interrupt register */\ *AT91C_US0_MR = AT91C_US_USMODE_RS485; /* Set UART to RUN RS485 Mode*/\ *AT91C_US0_MR &= ~AT91C_US_SYNC; /* Set UART in asynchronous mode */\ *AT91C_US0_MR |= AT91C_US_CLKS_CLOCK; /* Clock setup MCK*/\ *AT91C_US0_MR |= AT91C_US_CHRL_8_BITS; /* UART using 8-bit */\ *AT91C_US0_MR |= AT91C_US_PAR_NONE; /* UART using none parity bit */\ *AT91C_US0_MR |= AT91C_US_NBSTOP_1_BIT; /* UART using 1 stop bit */\ *AT91C_US0_MR |= AT91C_US_OVER; /* UART is using 8-bit sampling */\ *AT91C_US0_BRGR = ((OSC/8/BAUD_RATE) | (((OSC/8) - ((OSC/8/BAUD_RATE) * BAUD_RATE)) / ((BAUD_RATE + 4)/8)) << 16);\ *AT91C_US0_PTCR = (AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); /* Disable of TX & RX with DMA */\ *AT91C_US0_RCR = 0; /* Receive Counter Register */\ *AT91C_US0_TCR = 0; /* Transmit Counter Register */\ *AT91C_US0_RNPR = 0;\ *AT91C_US0_TNPR = 0;\ Tmp = *AT91C_US0_RHR;\ Tmp = *AT91C_US0_CSR;\ *AT91C_US0_RPR = (unsigned int)&(InBuf[InBufInPtr][0]); /* Initialise receiver buffer using DMA */\ *AT91C_US0_RCR = SIZE_OF_INBUF;\ *AT91C_US0_RNPR = (unsigned int)&(InBuf[(InBufInPtr + 1)%NO_OF_INBUFFERS][0]);\ *AT91C_US0_RNCR = SIZE_OF_INBUF;\ MsgIn = 0;\ InBufOutCnt = 0;\ *AT91C_US0_CR = AT91C_US_RXEN | AT91C_US_TXEN; /* Enable Tx & Rx on UART 0*/\ *AT91C_US0_PTCR = (AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); /* Enable of TX & RX with DMA */\ } #define HIGHSPEEDInitReceiver(InputBuffer)\ {\ UBYTE Tmp;\ pBuffer = InputBuffer;\ *AT91C_US0_PTCR = (AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); /* Disable of TX & RX with DMA */\ *AT91C_US0_RCR = 0; /* Receive Counter Register */\ *AT91C_US0_TCR = 0; /* Transmit Counter Register */\ *AT91C_US0_RNPR = 0;\ *AT91C_US0_TNPR = 0;\ Tmp = *AT91C_US0_RHR;\ Tmp = *AT91C_US0_CSR;\ Tmp = Tmp;\ *AT91C_US0_RPR = (unsigned int)&(InBuf[InBufInPtr][0]); /* Initialise receiver buffer using DMA */\ *AT91C_US0_RCR = SIZE_OF_INBUF;\ *AT91C_US0_RNPR = (unsigned int)&(InBuf[(InBufInPtr + 1)%NO_OF_INBUFFERS][0]);\ *AT91C_US0_RNCR = SIZE_OF_INBUF;\ MsgIn = 0;\ InBufOutCnt = 0;\ *AT91C_US0_CR = AT91C_US_RXEN | AT91C_US_TXEN; /* Enable Tx & Rx on UART 0*/\ *AT91C_US0_PTCR = (AT91C_PDC_RXTEN | AT91C_PDC_TXTEN); /* Enable of TX & RX with DMA */\ } #define HIGHSPEEDReceivedData(pByteCnt)\ {\ UWORD InCnt;\ *pByteCnt = 0;\ InCnt = (SIZE_OF_INBUF - *AT91C_US0_RCR);\ if (*AT91C_US0_RNCR == 0)\ {\ InCnt = SIZE_OF_INBUF;\ }\ InCnt -= InBufOutCnt; /* Remove already read bytes */\ if(InCnt)\ {\ while(InCnt > 0)\ {\ pBuffer[MsgIn] = InBuf[InBufInPtr][InBufOutCnt];\ MsgIn++;\ InBufOutCnt++;\ InCnt--;\ }\ *pByteCnt = MsgIn;\ MsgIn = 0;\ }\ if ((*AT91C_US0_RNCR == 0) && (SIZE_OF_INBUF == InBufOutCnt))\ {\ InBufOutCnt = 0;\ *AT91C_US0_RNPR = (unsigned int)InBufPtrs[InBufInPtr];\ *AT91C_US0_RNCR = SIZE_OF_INBUF;\ InBufInPtr = (InBufInPtr + 1) % NO_OF_INBUFFERS;\ }\ } #define AVAILOutBuf(Avail) if (!(*AT91C_US0_TNCR))\ {\ Avail = SIZE_OF_OUTBUF;\ }\ else\ {\ Avail = 0;\ } #define HIGHSPEEDSendDmaData(OutputBuffer, BytesToSend)\ {\ UWORD Avail, Cnt;\ AVAILOutBuf(Avail);\ if (BytesToSend < (Avail - 1))\ {\ for (Cnt = 0; Cnt < BytesToSend; Cnt++)\ {\ OutDma[DmaBufPtr][Cnt] = OutputBuffer[Cnt];\ }\ *AT91C_US0_TNPR = (unsigned int)&(OutDma[DmaBufPtr][0]);\ *AT91C_US0_TNCR = BytesToSend;\ DmaBufPtr = (DmaBufPtr + 1) % NO_OF_DMA_OUTBUFFERS;\ }\ } #define HIGHSPEEDExit {\ *AT91C_PMC_PCDR = PER_ID6_UART_0; /* Disable PMC clock for UART 0*/\ *AT91C_PIOA_PER = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN; /* Enable PIO on PA07, PA06 & PA05 */\ *AT91C_PIOA_PPUDR = HIGHSPEED_RX_PIN | HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN; /* Disable Pull-up resistor */\ *AT91C_PIOA_OER = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN; /* PA07 & PA06 set to Output */\ *AT91C_PIOA_CODR = HIGHSPEED_TX_PIN | HIGHSPEED_RTS_PIN | HIGHSPEED_RX_PIN; /* Set output low */\ } #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/d_input.c000066400000000000000000000050641466344546000166530ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-01-09 10:34 $ // // Filename $Workfile:: d_input.c $ // // Version $Revision:: 12 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_inpu $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "c_input.h" #include "d_input.h" #include "d_input.r" void dInputInit(void) { INPUTInit; } void dInputSetColorClkInput(void) { COLORClkInput; } void dInputGetAllColors(COLORSTRUCT *pRaw, UBYTE Status) { UPDATEAllColors(pRaw, Status); } void dInputGetRawAd(UWORD *pValues, UBYTE No) { INPUTGetVal(pValues, No); } void dInputSetDirOutDigi0(UBYTE Port) { INPUTSetOutDigi0(Port); } void dInputSetDirOutDigi1(UBYTE Port) { INPUTSetOutDigi1(Port); } void dInputSetDirInDigi0(UBYTE Port) { INPUTSetInDigi0(Port); } void dInputSetDirInDigi1(UBYTE Port) { INPUTSetInDigi1(Port); } void dInputClearDigi0(UBYTE Port) { INPUTClearDigi0(Port); INPUTSetOutDigi0(Port); } void dInputClearDigi1(UBYTE Port) { INPUTClearDigi1(Port); INPUTSetOutDigi1(Port); } void dInputSetDigi0(UBYTE Port) { INPUTSetDigi0(Port); INPUTSetOutDigi0(Port); } void dInputSetDigi1(UBYTE Port) { INPUTSetDigi1(Port); INPUTSetOutDigi1(Port); } void dInputRead0(UBYTE Port, UBYTE *pData) { INPUTReadDigi0(Port, pData); } void dInputRead1(UBYTE Port, UBYTE * pData) { INPUTReadDigi1(Port, pData); } void dInputSetActive(UBYTE Port) { INPUTSetActive(Port); } void dInputSet9v(UBYTE Port) { INPUTSet9v(Port); } void dInputSetInactive(UBYTE Port) { INPUTSetInactive(Port); } UBYTE dInputGetColor(UBYTE No, UWORD *pCol) { UBYTE Status; UPDATELed(No, pCol, Status); return(Status); } void dInputColorTx(UBYTE Port, UBYTE Data) { COLORTx(Port, Data); } void dInputReadCal(UBYTE Port, UBYTE *pData) { CALDataRead(Port, pData); } UBYTE dInputCheckColorStatus(UBYTE Port) { UBYTE Status; CHECKColorState(Port,Status); return(Status); } void dInputClearColor100msTimer(UBYTE No) { CLEARColor100msTimer(No); } UBYTE dInputChkColor100msTimer(UBYTE No) { UBYTE State; COLOR100msStatus(No, State); return(State); } void dInputExit(void) { INPUTExit; } nxt-firmware-1.29.7/src/d_input.h000066400000000000000000000027721466344546000166630ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-01-09 10:33 $ // // Filename $Workfile:: d_input.h $ // // Version $Revision:: 12 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_inpu $ // // Platform C // #ifndef D_INPUT #define D_INPUT void dInputInit(void); void dInputExit(void); void dInputGetRawAd(UWORD *pValues, UBYTE No); void dInputSetActive(UBYTE Port); void dInputSet9v(UBYTE Port); void dInputSetInactive(UBYTE Port); void dInputSetDirOutDigi0(UBYTE Port); void dInputSetDirOutDigi1(UBYTE Port); void dInputSetDirInDigi0(UBYTE Port); void dInputSetDirInDigi1(UBYTE Port); void dInputClearDigi0(UBYTE Port); void dInputClearDigi1(UBYTE Port); void dInputSetDigi0(UBYTE Port); void dInputSetDigi1(UBYTE Port); void dInputRead0(UBYTE Port, UBYTE *pData); void dInputRead1(UBYTE Port, UBYTE *pData); UBYTE dInputGetColor(UBYTE No, UWORD *pCol); void dInputColorTx(UBYTE Port, UBYTE Data); void dInputReadCal(UBYTE Port, UBYTE *pData); UBYTE dInputCheckColorStatus(UBYTE Port); void dInputGetAllColors(COLORSTRUCT *pRaw, UBYTE Status); void dInputSetColorClkInput(void); void dInputClearColor100msTimer(UBYTE No); UBYTE dInputChkColor100msTimer(UBYTE No); #endif nxt-firmware-1.29.7/src/d_input.r000066400000000000000000000442471466344546000167000ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-01-09 10:33 $ // // Filename $Workfile:: d_input.r $ // // Version $Revision:: 24 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_inpu $ // // Platform C // #ifdef SAM7S256 void rInputWait2uS(void); void rInputWait20uS(void); void rInputWait30uS(void); void rInputSingleADC(UBYTE Port, UWORD *Val); const ULONG Digi0Alloc[] = {AT91C_PIO_PA23, AT91C_PIO_PA28, AT91C_PIO_PA29, AT91C_PIO_PA30}; const ULONG Digi1Alloc[] = {AT91C_PIO_PA18, AT91C_PIO_PA19, AT91C_PIO_PA20, AT91C_PIO_PA2}; const ULONG ADPinDef[NO_OF_INPUTS] = {AT91C_ADC_CH1, AT91C_ADC_CH2, AT91C_ADC_CH3, AT91C_ADC_CH7}; unsigned int volatile* ADValRegs[NO_OF_INPUTS] = {AT91C_ADC_CDR1, AT91C_ADC_CDR2, AT91C_ADC_CDR3, AT91C_ADC_CDR7}; static UBYTE ColorReset[NO_OF_INPUTS]; static ULONG ColorClkDef; static ULONG ColorTimer[NO_OF_INPUTS]; #define TIME2US ((OSC/16)/500000L) #define TIME20US ((OSC/16)/50000L) #define TIME30US ((OSC/16)/33333L) #define TIME100MS ((OSC/16)/10L) #define MAX_AD_VALUE 0x3FF #define INPUTInit {\ UBYTE Tmp; \ for (Tmp = 0; Tmp < NOS_OF_AVR_INPUTS; Tmp++)\ { \ IoFromAvr.AdValue[Tmp] = MAX_AD_VALUE; \ } \ IoToAvr.InputPower = 0; \ for (Tmp = 0; Tmp < NO_OF_INPUTS; Tmp++) \ { \ *AT91C_PIOA_PPUDR = Digi0Alloc[Tmp]; \ *AT91C_PIOA_PPUDR = Digi1Alloc[Tmp]; \ INPUTSetInDigi0(Tmp); \ INPUTSetInDigi1(Tmp); \ ColorReset[Tmp] = FALSE; \ } \ ColorClkDef = 0; \ } #define INPUTGetVal(pValues, No) *pValues = (UWORD)IoFromAvr.AdValue[No]; \ *pValues &= 0x03FF #define INPUTSetActive(Input) IoToAvr.InputPower |= (0x01 << Input); \ IoToAvr.InputPower &= ~(0x10 << Input) #define INPUTSet9v(Input) IoToAvr.InputPower |= (0x10 << Input); \ IoToAvr.InputPower &= ~(0x01 << Input) #define INPUTSetInactive(Input) IoToAvr.InputPower &= ~(0x11 << Input) #define INPUTSetOutDigi0(Input) *AT91C_PIOA_PER = Digi0Alloc[Input]; \ *AT91C_PIOA_OER = Digi0Alloc[Input] #define INPUTSetOutDigi1(Input) *AT91C_PIOA_PER = Digi1Alloc[Input]; \ *AT91C_PIOA_OER = Digi1Alloc[Input] #define INPUTSetInDigi0(Input) *AT91C_PIOA_PER = Digi0Alloc[Input]; \ *AT91C_PIOA_ODR = Digi0Alloc[Input] #define INPUTSetInDigi1(Input) *AT91C_PIOA_PER = Digi1Alloc[Input]; \ *AT91C_PIOA_ODR = Digi1Alloc[Input] #define INPUTSetDigi0(Input) *AT91C_PIOA_SODR = Digi0Alloc[Input] #define INPUTSetDigi1(Input) *AT91C_PIOA_SODR = Digi1Alloc[Input] #define INPUTClearDigi0(Input) *AT91C_PIOA_CODR = Digi0Alloc[Input] #define INPUTClearDigi1(Input) *AT91C_PIOA_CODR = Digi1Alloc[Input] #define INPUTReadDigi0(Input, Data) if ((*AT91C_PIOA_PDSR) & Digi0Alloc[Input]) \ { \ *Data |= 0x00000001; \ } \ else \ { \ *Data &= ~0x00000001; \ } #define INPUTReadDigi1(Input, Data) if ((*AT91C_PIOA_PDSR) & Digi1Alloc[Input]) \ { \ *Data |= 0x00000002; \ } \ else \ { \ *Data &= ~0x00000002; \ } #define INPUTClkHigh(Port) INPUTSetDigi0(Port); \ INPUTSetOutDigi0(Port); \ rInputWait2uS() #define INPUTClkLow(Port) INPUTClearDigi0(Port); \ INPUTSetOutDigi0(Port); \ rInputWait2uS() #define COLORClkInput *AT91C_PIOA_ODR = ColorClkDef #define UPDATEAllColors(Vals, Status){\ ULONG ADDef; \ ADDef = 0; \ ColorClkDef = 0; \ if (0x01 & Status) \ { \ ADDef |= ADPinDef[0]; \ ColorClkDef |= Digi0Alloc[0]; \ if ((*AT91C_PIOA_PDSR) & Digi0Alloc[0]) \ { \ ColorReset[0] = TRUE; \ } \ } \ if (0x02 & Status) \ { \ ADDef |= ADPinDef[1]; \ ColorClkDef |= Digi0Alloc[1]; \ if ((*AT91C_PIOA_PDSR) & Digi0Alloc[1]) \ { \ ColorReset[1] = TRUE; \ } \ } \ if (0x04 & Status) \ { \ ADDef |= ADPinDef[2]; \ ColorClkDef |= Digi0Alloc[2]; \ if ((*AT91C_PIOA_PDSR) & Digi0Alloc[2]) \ { \ ColorReset[2] = TRUE; \ } \ } \ if (0x08 & Status) \ { \ ADDef |= ADPinDef[3]; \ ColorClkDef |= Digi0Alloc[3]; \ if ((*AT91C_PIOA_PDSR) & Digi0Alloc[3]) \ { \ ColorReset[3] = TRUE; \ } \ } \ *AT91C_PIOA_OER = ColorClkDef; \ *AT91C_ADC_CHER = ADDef; \ GetAdVals(Vals, BLANK, Status); \ *AT91C_PIOA_SODR = ColorClkDef; \ rInputWait20uS(); \ GetAdVals(Vals, RED, Status); \ *AT91C_PIOA_CODR = ColorClkDef; \ rInputWait20uS(); \ GetAdVals(Vals, GREEN, Status); \ *AT91C_PIOA_SODR = ColorClkDef; \ rInputWait20uS(); \ GetAdVals(Vals, BLUE, Status); \ *AT91C_PIOA_CODR = ColorClkDef; \ *AT91C_ADC_CHDR = ADDef; \ } #define UPDATELed(Port, Col, Status) { \ rInputSingleADC(Port, Col); \ if ((*AT91C_PIOA_PDSR) & Digi0Alloc[Port]) \ { \ ColorReset[Port] = TRUE; \ } \ CHECKColorState(Port, Status); \ } #define SETClkHi(Port) INPUTClkHigh(Port) \ #define COLORTx(Port, Data) { \ UBYTE BitCnt; \ BitCnt = 0; \ while(BitCnt++ < 8) \ { \ INPUTClkHigh(Port); \ if (Data & 0x01) \ { \ INPUTSetDigi1(Port); \ } \ else \ { \ INPUTClearDigi1(Port); \ } \ rInputWait30uS(); \ Data >>= 1; \ INPUTClkLow(Port); \ rInputWait30uS(); \ } \ } #define CALDataRead(Port, pData) {\ UBYTE BitCnt; \ UBYTE Data = 0; \ BitCnt = 0; \ INPUTClkHigh(Port); \ rInputWait2uS(); \ while(BitCnt++ < 8) \ { \ INPUTClkHigh(Port); \ rInputWait2uS(); \ rInputWait2uS(); \ INPUTClkLow(Port); \ Data >>= 1; \ if ((*AT91C_PIOA_PDSR) & Digi1Alloc[Port])\ { \ Data |= 0x80; \ } \ rInputWait2uS(); \ } \ *pData = Data; \ } #define CHECKColorState(Port, Status) {\ Status = TRUE; \ if ((IoFromAvr.AdValue[Port] > 50) || (TRUE == ColorReset[Port])) \ { \ Status = FALSE; \ ColorReset[Port] = FALSE; \ } \ } #define INPUTExit { \ UBYTE Tmp; \ *AT91C_ADC_CHDR = (AT91C_ADC_CH1 | AT91C_ADC_CH2 | AT91C_ADC_CH3 | AT91C_ADC_CH7);\ for (Tmp = 0; Tmp < NO_OF_INPUTS; Tmp++) \ { \ INPUTSetInDigi0(Tmp); \ INPUTSetInDigi1(Tmp); \ } \ } #define CLEARColor100msTimer(No) ColorTimer[No] = (*AT91C_PITC_PIIR);\ #define COLOR100msStatus(No,V) V = FALSE;\ if (((*AT91C_PITC_PIIR) - ColorTimer[No]) > TIME100MS)\ {\ V = TRUE;\ } void rInputSingleADC(UBYTE Port, UWORD *Val) { *Val = *AT91C_ADC_LCDR; *AT91C_ADC_CHER = ADPinDef[Port]; ADStart; while(!((*AT91C_ADC_SR) & AT91C_ADC_DRDY)); *Val = *AT91C_ADC_LCDR; *AT91C_ADC_CHDR = ADPinDef[Port]; } void GetAdVals(COLORSTRUCT *pColStruct, UBYTE Color, UBYTE Status) { UBYTE ChCnt; ADStart; for(ChCnt = 0; ChCnt < NO_OF_INPUTS; ChCnt++) { if (Status & (0x01 << ChCnt)) { while(!((*AT91C_ADC_SR) & ADPinDef[ChCnt])); pColStruct[ChCnt].ADRaw[Color] = *ADValRegs[ChCnt]; } } ADStart; for(ChCnt = 0; ChCnt < NO_OF_INPUTS; ChCnt++) { if (Status & (0x01 << ChCnt)) { while(!((*AT91C_ADC_SR) & ADPinDef[ChCnt])); pColStruct[ChCnt].ADRaw[Color] += *ADValRegs[ChCnt]; pColStruct[ChCnt].ADRaw[Color] = (pColStruct[ChCnt].ADRaw[Color])>>1; } } } void rInputWait2uS(void) { ULONG PitTmr; PitTmr = (*AT91C_PITC_PIIR); while (((*AT91C_PITC_PIIR) - PitTmr) < TIME2US); } void rInputWait20uS(void) { ULONG PitTmr; PitTmr = (*AT91C_PITC_PIIR); while (((*AT91C_PITC_PIIR) - PitTmr) < TIME20US); } void rInputWait30uS(void) { ULONG PitTmr; PitTmr = (*AT91C_PITC_PIIR); while (((*AT91C_PITC_PIIR) - PitTmr) < TIME30US); } #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/d_ioctrl.c000066400000000000000000000014651466344546000170110ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 5-12-07 15:23 $ // // Filename $Workfile:: d_ioctrl.c $ // // Version $Revision:: 2 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_ioct $ // // Platform C // #include #include "stdconst.h" #include "m_sched.h" #include "d_ioctrl.h" #include "d_ioctrl.r" void dIOCtrlInit(void) { IOCTRLInit; } void dIOCtrlSetPower(UBYTE Power) { INSERTPower(Power); } void dIOCtrlSetPwm(UBYTE Pwm) { INSERTPwm(Pwm); } void dIOCtrlTransfer(void) { I2CTransfer; } void dIOCtrlExit(void) { IOCTRLExit; } nxt-firmware-1.29.7/src/d_ioctrl.h000066400000000000000000000011671466344546000170150ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_ioctrl.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_ioct $ // // Platform C // #ifndef D_AVRCOMM #define D_AVRCOMM void dIOCtrlInit(void); void dIOCtrlExit(void); void dIOCtrlSetPower(UBYTE Power); void dIOCtrlSetPwm(UBYTE Pwm); void dIOCtrlTransfer(void); #endif nxt-firmware-1.29.7/src/d_ioctrl.r000066400000000000000000000261011466344546000170220ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 7-12-07 14:09 $ // // Filename $Workfile:: d_ioctrl.r $ // // Version $Revision:: 4 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_ioct $ // // Platform C // #ifdef SAM7S256 extern void I2cHandler(void); enum { I2C_IDLE = 1, I2C_ERROR = 2, I2C_TX = 3, I2C_RX = 4 }; #define NO_TO_TX BYTES_TO_TX + 1 #define NO_TO_RX BYTES_TO_RX + 1 #define TIMEOUT (((OSC/16)/1000)*30) /* 100 ms timeout on I2C*/ #define I2CCLK 400000L #define TIME400KHZ (((OSC/16L)/(I2CCLK * 2)) + 1) #define CLDIV (((OSC/I2CCLK)/2)-3) #define DEVICE_ADR 0x01 static UBYTE *pIrq; static UBYTE volatile Cnt; static UBYTE I2cStatus; static UBYTE I2cLastStatus; static UBYTE I2cInBuffer[NO_TO_RX]; static UBYTE I2cOutBuffer[COPYRIGHTSTRINGLENGTH + 1]; static UBYTE RxSum; static ULONG I2CTimerValue; #define DISABLEI2cIrqs *AT91C_TWI_IDR = 0x000001C7 #define ISSUEStopCond *AT91C_TWI_CR = AT91C_TWI_STOP #define INSERTPower(Power) IoToAvr.Power = Power #define INSERTPwm(Pwm) IoToAvr.PwmFreq = Pwm #define SETTime I2CTimerValue = ((*AT91C_PITC_PIIR) & AT91C_PITC_CPIV) #define DISABLETwi *AT91C_PIOA_PPUDR = (AT91C_PA4_TWCK | AT91C_PA3_TWD);/* no pull up */\ *AT91C_PIOA_MDER = (AT91C_PA4_TWCK | AT91C_PA3_TWD);/* SCL + SDA is open drain*/\ *AT91C_PIOA_SODR = (AT91C_PA4_TWCK | AT91C_PA3_TWD);/* SCL + SDA is high */\ *AT91C_PIOA_OER = (AT91C_PA4_TWCK | AT91C_PA3_TWD);/* SCL + SDA is output */\ *AT91C_PIOA_PER = (AT91C_PA4_TWCK | AT91C_PA3_TWD);/* Disable peripheal */\ #define STARTIrqTx I2cStatus = I2C_TX;\ I2cLastStatus = I2C_TX;\ pIrq = I2cOutBuffer;\ *AT91C_TWI_CR = AT91C_TWI_MSEN;\ *AT91C_TWI_MMR = (AT91C_TWI_IADRSZ_NO | (DEVICE_ADR << 16)); /* no int. adr, write dir */\ *AT91C_TWI_IER = 0x00000104; /* Enable TX related irq */\ *AT91C_TWI_THR = *pIrq #define WAITClk {\ ULONG PitTmr;\ PitTmr = (*AT91C_PITC_PIIR & AT91C_PITC_CPIV) + TIME400KHZ;\ if (PitTmr >= (*AT91C_PITC_PIMR & AT91C_PITC_CPIV))\ {\ PitTmr -= (*AT91C_PITC_PIMR & AT91C_PITC_CPIV);\ }\ while ((*AT91C_PITC_PIIR & AT91C_PITC_CPIV) < PitTmr);\ } #define RESETI2c {\ UBYTE Tmp;\ DISABLETwi;\ Tmp = 0;\ /* Clock minimum 9 times and both SCK and SDA should be high */\ while((!(*AT91C_PIOA_PDSR & AT91C_PA3_TWD)) || (Tmp <= 9))\ {\ if ((*AT91C_PIOA_PDSR) & AT91C_PA4_TWCK) /* Clk strectching? */\ {\ *AT91C_PIOA_CODR = AT91C_PA4_TWCK; /* SCL is low */\ WAITClk;\ *AT91C_PIOA_SODR = AT91C_PA4_TWCK; /* SCL is high */\ WAITClk;\ Tmp++;\ }\ }\ *AT91C_TWI_CR = AT91C_TWI_MSDIS;\ *AT91C_TWI_CR = AT91C_TWI_SWRST;\ *AT91C_PIOA_ASR = (AT91C_PA4_TWCK | AT91C_PA3_TWD); /* Sel. per. A */\ *AT91C_PIOA_PDR = (AT91C_PA4_TWCK | AT91C_PA3_TWD); /* Sel. per on pins*/\ } #define IOCTRLInit *AT91C_AIC_IDCR = (1L< #include #define FILEVERSION (0x0000010DL) #define MAX_FILES ((FILETABLE_SIZE) - 1) /* Last file entry is used for file version*/ #define FILEVERSIONINDEX ((FILETABLE_SIZE) - 1) /* Last file entry is used for file version*/ #define MAX_WRITE_BUFFERS 4 #define FLASHOFFSET (0x100000L) #define IS_LOADER_ERR(LStatus) (((LStatus) & 0xFF00) != SUCCESS) #define SECTORINDEXUSERFLASH ((STARTOFUSERFLASH & ~FLASHOFFSET)/256/32) #define SECTORPOINTERUSERFLASH (((STARTOFUSERFLASH & ~FLASHOFFSET) - ((SECTORINDEXUSERFLASH * 32) * 256))/256) typedef struct { const UBYTE *pFlash; const UWORD *pSectorNo; ULONG ReadLength; ULONG DataLength; ULONG FileDlPtr; UBYTE SearchStr[FILENAME_SIZE]; UWORD FileIndex; UWORD CheckSum; UBYTE SearchType; UBYTE Status; UBYTE FileType; UBYTE WriteBufNo; }HANDLE; typedef struct { ULONG Buf[SECTORSIZE/4]; UBYTE BufIndex; UBYTE Status; }WRITEBUF; static HANDLE HandleTable[MAX_HANDLES]; static WRITEBUF WriteBuffer[MAX_WRITE_BUFFERS]; static ULONG SectorTable[NOOFSECTORS>>5]; static FILEHEADER Header; static ULONG FreeUserFlash; static UWORD FreeSectors; void dLoaderUpdateSectorTable(void); UWORD dLoaderGetFreeHandle(void); const UBYTE * dLoaderGetNextSectorPtr(UBYTE Handle); ULONG dLoaderReturnFreeFlash(void); UWORD dLoaderAllocateHeader(UWORD Handle, ULONG *FileStartAdr, FILEHEADER *pHeader, UWORD HeaderByteSize, UWORD CompleteFileSectorSize); UWORD dLoaderFlashFileHeader(UWORD Handle, ULONG FileStartAdr, FILEHEADER *pHeader, UWORD HeaderByteSize); UWORD dLoaderFindFirstSector(UBYTE Type, UWORD SectorCount, UWORD *pSector); UWORD dLoaderAllocateWriteBuffer(UWORD Handle); UWORD dLoaderSetFilePointer(UWORD Handle, ULONG BytePtr, const UBYTE **pData); UWORD dLoaderGetSectorNumber(ULONG Adr); void dLoaderCheckVersion(void); UWORD dLoaderCheckHandle(UWORD Handle, UBYTE Operation); ULONG dLoaderCalcFreeFileSpace(UWORD NosOfFreeSectors); UWORD dLoaderCheckDownload(UBYTE *pName); UWORD dLoaderAvailFileNo(void); void dLoaderInit(void) { UWORD Tmp; LOADERInit; /* Clear handle table */ for (Tmp = 0; Tmp < MAX_HANDLES; Tmp++) { HandleTable[Tmp].Status = FREE; HandleTable[Tmp].WriteBufNo = FREEBUFNO; } /* Clear write buffers */ for (Tmp = 0; Tmp < MAX_WRITE_BUFFERS; Tmp++) { WriteBuffer[Tmp].Status = FREE; } dLoaderCheckVersion(); dLoaderUpdateSectorTable(); } UWORD dLoaderAvailFileNo(void) { UBYTE Tmp, Tmp2; UWORD ReturnVal; ReturnVal = NOMOREFILES; Tmp2 = 0; for(Tmp = 0; Tmp < MAX_HANDLES; Tmp++) { /* Check for files allready downloading except datafiles as the have entered their */ /* filepointer in the filepointer table at begin of download */ if ((DOWNLOADING == HandleTable[Tmp].Status) && (DATAFILE != HandleTable[Tmp].FileType)) { Tmp2++; } } if ((0xFFFFFFFF == FILEPTRTABLE[(MAX_FILES - 1) - Tmp2]) || (0 == FILEPTRTABLE[(MAX_FILES - 1) - Tmp2])) { ReturnVal = SUCCESS; } return(ReturnVal); } void dLoaderWriteFilePtrTable(ULONG *RamFilePtrTable) { UWORD TmpTableSize; /* FILETABLE_SIZE is in LONG */ TmpTableSize = (FILETABLE_SIZE * 4); while(TmpTableSize) { TmpTableSize -= SECTORSIZE; dLoaderWritePage((ULONG)FILEPTRTABLE + TmpTableSize, SECTORSIZE, RamFilePtrTable + (TmpTableSize/4)); } } UWORD dLoaderInsertPtrTable(const UBYTE *pAdr, UWORD Handle) { UWORD TmpCnt; UWORD Status; ULONG PtrTable[FILETABLE_SIZE]; /* It is possible to add the file as checking for number of files */ /* is done when initiating the file download */ memset(PtrTable, 0, sizeof(PtrTable)); TmpCnt = MAX_FILES - 1; while(TmpCnt) { /* TmpCnt-- first because you want to copy from index 0 */ TmpCnt--; PtrTable[TmpCnt + 1] = FILEPTRTABLE[TmpCnt]; } /* Copy the new file in position 0 */ PtrTable[0] = (ULONG)pAdr; /* Add the File version to the top of the file list */ PtrTable[FILEVERSIONINDEX] = FILEPTRTABLE[FILEVERSIONINDEX]; /* Write the file pointer table to flash */ dLoaderWriteFilePtrTable(PtrTable); /* FileIndex in HandleTable should be incremented by one - new file index is 0 */ for (TmpCnt = 0; TmpCnt < MAX_HANDLES; TmpCnt++) { if (HandleTable[TmpCnt].Status != FREE) { (HandleTable[TmpCnt].FileIndex)++; } } HandleTable[Handle].FileIndex = 0; Status = SUCCESS | Handle; return(Status); } UWORD dLoaderDeleteFilePtr(UWORD Handle) { UWORD ErrorCode; UWORD LongCnt; ULONG PtrTable[FILETABLE_SIZE]; ErrorCode = SUCCESS; if (0xFFFFFFFF != FILEPTRTABLE[HandleTable[Handle].FileIndex]) { ErrorCode = dLoaderCheckFiles(Handle); if (0x8000 > ErrorCode) { for (LongCnt = 0; LongCnt < (HandleTable[Handle].FileIndex); LongCnt++) { PtrTable[LongCnt] = FILEPTRTABLE[LongCnt]; } /* Skip the file that has to be deleted "LongCnt + 1" */ for ( ; LongCnt < (MAX_FILES - 1); LongCnt++) { PtrTable[LongCnt] = FILEPTRTABLE[LongCnt+1]; } /* The top file entry is now free */ PtrTable[MAX_FILES - 1] = 0xFFFFFFFF; /* Insert the file version */ PtrTable[MAX_FILES] = FILEPTRTABLE[MAX_FILES]; /* Write the file pointer table back into flash */ dLoaderWriteFilePtrTable(PtrTable); dLoaderUpdateSectorTable(); /* Update the HandleTable[].FileIndex */ for (LongCnt = 0; LongCnt < MAX_HANDLES; LongCnt++) { /* FileIndex must not be decremented for to the file to be deleted (when Handle = LongCnt)*/ if ((HandleTable[Handle].FileIndex < HandleTable[LongCnt].FileIndex) && (FREE != HandleTable[LongCnt].Status)) { (HandleTable[LongCnt].FileIndex)--; } } } } else { ErrorCode = FILENOTFOUND; } return(ErrorCode | Handle); } void dLoaderDeleteAllFiles(void) { ULONG Tmp; ULONG PtrTable[FILETABLE_SIZE]; /* Close all handles - all files is to be wiped out */ for (Tmp = 0; Tmp < MAX_HANDLES; Tmp++) { dLoaderCloseHandle(Tmp); } for (Tmp = ((STARTOFUSERFLASH-FLASHOFFSET)/SECTORSIZE); Tmp < (SIZEOFFLASH/SECTORSIZE); Tmp++) { dLoaderErasePage(Tmp<>5] |= (0x1 << (SectorNo & 0x001F)); Tmp += (SECTORSIZE >> 2); } for (Tmp = 0; Tmp < MAX_FILES; Tmp++) { if ((0xFFFFFFFF != FILEPTRTABLE[Tmp]) && (0x00000000 != FILEPTRTABLE[Tmp])) { pFile = (const FILEHEADER *) FILEPTRTABLE[Tmp]; /* This is necessary if the start address is at the first address in an sector */ SectorNo = dLoaderGetSectorNumber((ULONG)pFile->FileStartAdr); SectorTable[SectorNo>>5] |= (0x1 << (SectorNo & 0x001F)); /* This is necessary as the first sector (where the fileheader is) is not */ /* included in the sector table */ SectorNo = dLoaderGetSectorNumber((ULONG)FILEPTRTABLE[Tmp]); SectorTable[SectorNo>>5] |= (0x1 << (SectorNo & 0x001F)); /* First Sector with data has been allocated add this as the initial */ /* file size */ FileSize = SECTORSIZE - ((pFile->FileStartAdr) & (SECTORSIZE-1)) ; pSectorTable = pFile->FileSectorTable; while((FileSize < (pFile->FileSize)) && (NOOFSECTORS > (*pSectorTable))) { SectorTable[(*pSectorTable)>>5] |= (0x1 << ((*pSectorTable) & 0x1F)); if (0 == ((ULONG)(pSectorTable + 1) & (SECTORSIZE-1))) { pSectorTable = (UWORD*)(((ULONG)(*pSectorTable) << SECTORSIZESHIFT) | FLASHOFFSET); } else { *pSectorTable++; FileSize += SECTORSIZE; } } } } FreeUserFlash = dLoaderReturnFreeFlash(); } UWORD dLoaderCreateFileHeader(ULONG FileSize, UBYTE *pName, UBYTE LinearState, UBYTE FileType) { UWORD HeaderByteSize; ULONG FileStartAdr; ULONG CompleteFileByteSize; UWORD Handle; UBYTE Name[FILENAME_SIZE]; ULONG FileLength; ULONG DataLength; UWORD ErrorCode; UWORD CompleteSectorNo; UWORD Tmp; memset(&(Header.FileName), 0, sizeof(Header.FileName)); memset(&(Header.FileSectorTable), 0xFF, sizeof(Header.FileSectorTable)); ErrorCode = dLoaderFind(pName, Name, &FileLength, &DataLength, (UBYTE)BUSY); Handle = ErrorCode & 0x00FF; if (SUCCESS == (ErrorCode & 0xFF00)) { ErrorCode |= FILEEXISTS; } if (FILENOTFOUND == (ErrorCode & 0xFF00)) { /* Here check for the download buffers for a matching download */ /* in progress */ ErrorCode &= 0x00FF; ErrorCode = dLoaderCheckDownload(pName); if (0x8000 > ErrorCode) { /* Check for file overflow */ ErrorCode = dLoaderAvailFileNo(); if (0x8000 > ErrorCode) { ErrorCode = dLoaderAllocateWriteBuffer(Handle); if (0x8000 > ErrorCode) { dLoaderCopyFileName((Header.FileName), pName); HandleTable[Handle].pSectorNo = 0; HandleTable[Handle].DataLength = FileSize; /* used for end of file detection */ Header.FileSize = FileSize; /* used to program into flash */ if (DATAFILE == FileType) { Header.DataSize = 0; } else { Header.DataSize = FileSize; } HandleTable[Handle].FileType = FileType | LinearState; /* if it is a datafile it can be stopped */ Header.FileType = FileType | LinearState; /* FileType included for future appending */ /* File body size calculation*/ CompleteFileByteSize = FileSize; /* Add the the fixed header to the fixed size */ CompleteFileByteSize += HEADERFIXEDSIZE; /* Find the number of sectors used by the fixed file size */ CompleteSectorNo = (CompleteFileByteSize - 1) >> SECTORSIZESHIFT; /* Add the size taken by the sectortable */ CompleteFileByteSize += (CompleteSectorNo << 1); /* Recalc the number of sectors - to see wether the sectortable has caused */ /* the sectornumber to encrease */ Tmp = ((CompleteFileByteSize - 1) >> SECTORSIZESHIFT); /* Substract from the original sectors used - Tmp holds the encreased number*/ Tmp -= CompleteSectorNo; if (Tmp) { /* The sectortable takes up more than one sector */ CompleteFileByteSize += Tmp << 1; CompleteSectorNo += Tmp; } HeaderByteSize = CompleteFileByteSize - FileSize; if (HeaderByteSize & 0x0003) { /* Header size is not a multiplum of 4 - now make it a mul 4 */ HeaderByteSize += (0x0004 - (HeaderByteSize & 0x0003)); } if (FileSize <= FreeUserFlash) { /* Allocate file header */ Tmp = (((CompleteFileByteSize - 1) >> SECTORSIZESHIFT) + 1); Handle = dLoaderAllocateHeader(Handle, &FileStartAdr, &Header, HeaderByteSize, Tmp); if (Handle < 0x8000) { dLoaderFlashFileHeader(Handle, FileStartAdr, &Header, HeaderByteSize); /* If this is a datafile then add the filepointer to the filepointer table */ /* now as the datafile do not need to have a special size to be valid */ if (DATAFILE & HandleTable[Handle].FileType) { ErrorCode = dLoaderInsertPtrTable((const UBYTE *)HandleTable[Handle].FileDlPtr, Handle); } FreeSectors -= Tmp; FreeUserFlash = dLoaderCalcFreeFileSpace(FreeSectors); HandleTable[Handle].Status = DOWNLOADING; } } else { ErrorCode = NOSPACE; } } } } } return(ErrorCode | Handle); } UWORD dLoaderWriteData(UWORD Handle, UBYTE *pBuf, UWORD *pLen) { UWORD Tmp; UBYTE *pSectorBuf; Handle = dLoaderCheckHandle(Handle, DOWNLOADING); if (0x8000 > Handle) { if (*pLen > HandleTable[Handle].DataLength) { /* Write request exceeds filesize - only flash up to filesize*/ *pLen = HandleTable[Handle].DataLength; WriteBuffer[HandleTable[Handle].WriteBufNo].Status = DLERROR; /* save error untill close handle */ } pSectorBuf = (UBYTE *)WriteBuffer[HandleTable[Handle].WriteBufNo].Buf; for(Tmp = 0; Tmp < *pLen; Tmp++) { pSectorBuf[WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex] = pBuf[Tmp]; if ((WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex) >= (SECTORSIZE-1)) { dLoaderWritePage(((ULONG)HandleTable[Handle].pFlash & ~(SECTORSIZE - 1)), SECTORSIZE, WriteBuffer[HandleTable[Handle].WriteBufNo].Buf); WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex = 0; HandleTable[Handle].pFlash = dLoaderGetNextSectorPtr(Handle); memset(WriteBuffer[HandleTable[Handle].WriteBufNo].Buf, 0xFF, sizeof(WriteBuffer[0].Buf)); } else { (WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex)++; } } HandleTable[Handle].DataLength -= *pLen; /* Check for correct end of file */ if (0 == HandleTable[Handle].DataLength) { if ((WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex) != 0) { /* write the last data into the file */ dLoaderWritePage(((ULONG)HandleTable[Handle].pFlash & ~(SECTORSIZE - 1)), WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex, WriteBuffer[HandleTable[Handle].WriteBufNo].Buf); WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex = 0; } } } else { *pLen = 0; } if (DLERROR == WriteBuffer[HandleTable[Handle].WriteBufNo].Status) { /* DLERROR set due to over flow a file - EOFEXSPECTED should be set */ /* for repeated overflow requests */ Handle |= EOFEXSPECTED; } return(Handle); } UWORD dLoaderGetFreeHandle(void) { UBYTE Tmp; UWORD Handle; Handle = NOMOREHANDLES; for(Tmp = 0; Tmp < MAX_HANDLES; Tmp++) { if (FREE == HandleTable[Tmp].Status) { HandleTable[Tmp].Status = BUSY; Handle = 0; /* Clear NOMOREHANDLES */ Handle = Tmp; Tmp = MAX_HANDLES; } } return(Handle); } const UBYTE * dLoaderGetNextSectorPtr(UBYTE Handle) { const UBYTE *pAdr; /* Check for the last entry in a sector - if so, */ /* then this is the sector number on the next sector table */ if (!((ULONG)(HandleTable[Handle].pSectorNo + 1) & (SECTORSIZE-1))) { HandleTable[Handle].pSectorNo = (const UWORD *)(((ULONG)(*(HandleTable[Handle].pSectorNo)) << SECTORSIZESHIFT) | FLASHOFFSET); } /* If pointing at an illegal adr then set it to NULL */ if (SIZEOFFLASH < (ULONG)((ULONG)(HandleTable[Handle].pSectorNo) & ~FLASHOFFSET)) { pAdr = NULL; } else { pAdr = (const UBYTE *)(((ULONG)(*(HandleTable[Handle].pSectorNo)) << SECTORSIZESHIFT) | FLASHOFFSET); } (HandleTable[Handle].pSectorNo)++; return(pAdr); } UWORD dLoaderCloseHandle(UWORD Handle) { UWORD RtnStatus; FILEHEADER *TmpFileHeader; RtnStatus = Handle; /* if it is a normal handle or handle closed due to an error then error must be different */ /* from the no more handles available error (else you would delete a used handle) */ if (((0x8000 > Handle) || (NOMOREHANDLES != (Handle & 0xFF00))) && ((UBYTE)Handle < MAX_HANDLES)) { Handle &= 0x00FF; if (FREE == HandleTable[Handle].Status) { RtnStatus |= HANDLEALREADYCLOSED; } else { /* Handle was NOT free - now close it */ if (DOWNLOADING == HandleTable[Handle].Status) { if (DATAFILE & HandleTable[Handle].FileType) { /* This is a Datafile that should be closed and this is a legal action */ /* 1. Write the data from the writebuffer into flash */ /* 2. Update the Datalength in the file header */ /* This takes minimum 8 mS (2 page writes into flash) */ if (WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex) { /* There are databytes in the writebuffer write them into flash */ dLoaderWritePage(((ULONG)HandleTable[Handle].pFlash & ~(SECTORSIZE - 1)), WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex, WriteBuffer[HandleTable[Handle].WriteBufNo].Buf); } /* Now the databuffer is free now use if for a buffer for the fileheader*/ memcpy(WriteBuffer[HandleTable[Handle].WriteBufNo].Buf, (void const*)HandleTable[Handle].FileDlPtr, SECTORSIZE); TmpFileHeader = (FILEHEADER *) WriteBuffer[HandleTable[Handle].WriteBufNo].Buf; TmpFileHeader->DataSize = TmpFileHeader->FileSize - HandleTable[Handle].DataLength; dLoaderWritePage(((ULONG)HandleTable[Handle].FileDlPtr & ~(SECTORSIZE - 1)), SECTORSIZE, WriteBuffer[HandleTable[Handle].WriteBufNo].Buf); } else { /* This is a system file being closed now update the file pointer table if no error and complete file written */ if ((DLERROR != WriteBuffer[HandleTable[Handle].WriteBufNo].Status) && (0 == HandleTable[Handle].DataLength)) { /* no error durig download - add the file pointer to the file pointer table */ Handle = dLoaderInsertPtrTable((const UBYTE *) HandleTable[Handle].FileDlPtr, Handle); } else { /* an error has occured during download - now clean up the mess... */ dLoaderUpdateSectorTable(); } } } if (HandleTable[Handle].WriteBufNo != FREEBUFNO) { WriteBuffer[HandleTable[Handle].WriteBufNo].Status = FREE; HandleTable[Handle].WriteBufNo = FREEBUFNO; } HandleTable[Handle].Status = FREE; } } return(RtnStatus); } UWORD dLoaderOpenRead(UBYTE *pFileName, ULONG *pLength) { UWORD Handle; UBYTE Name[16]; const FILEHEADER *TmpHeader; ULONG FileLength; ULONG DataLength; Handle = dLoaderFind(pFileName, Name, &FileLength, &DataLength, (UBYTE)BUSY); if (0x8000 > Handle) { if (FileLength) { TmpHeader = (FILEHEADER const *)(FILEPTRTABLE[HandleTable[Handle].FileIndex]); HandleTable[Handle].pFlash = (const UBYTE *)TmpHeader->FileStartAdr; HandleTable[Handle].pSectorNo = TmpHeader->FileSectorTable; HandleTable[Handle].DataLength = TmpHeader->DataSize; HandleTable[Handle].ReadLength = 0; *pLength = TmpHeader->DataSize; } else { Handle = FILENOTFOUND; } } return(Handle); } UWORD dLoaderRead(UBYTE Handle, UBYTE *pBuffer, ULONG *pLength) { UWORD ByteCnt, Status; Status = dLoaderCheckHandle(Handle, BUSY); if (0x8000 > Status) { Status = Handle; ByteCnt = 0; while (ByteCnt < *pLength) { if (HandleTable[Handle].DataLength <= HandleTable[Handle].ReadLength) { *pLength = ByteCnt; Status |= ENDOFFILE; } else { *pBuffer = *(HandleTable[Handle].pFlash); pBuffer++; ByteCnt++; HandleTable[Handle].pFlash++; HandleTable[Handle].ReadLength++; if (!((ULONG)(HandleTable[Handle].pFlash) & (SECTORSIZE-1))) { HandleTable[Handle].pFlash = dLoaderGetNextSectorPtr(Handle); } } } } return(Status); } UWORD dLoaderDelete(UBYTE *pFile) { UWORD LStatus; ULONG FileLength; ULONG DataLength; UBYTE Name[FILENAME_LENGTH + 1]; LStatus = dLoaderFind(pFile, Name, &FileLength, &DataLength, (UBYTE)BUSY); if (!IS_LOADER_ERR(LStatus)) { LStatus = dLoaderDeleteFilePtr((UBYTE)LStatus); } dLoaderCloseHandle(LStatus); return(LStatus); } UWORD dLoaderFind(UBYTE *pFind, UBYTE *pFound, ULONG *pFileLength, ULONG *pDataLength, UBYTE Session) { UWORD Handle; Handle = dLoaderGetFreeHandle(); if (Handle < 0x8000) { if (FILENAME_LENGTH < strlen((const char*)pFind)) { Handle |= ILLEGALFILENAME; } else { HandleTable[Handle].FileIndex = 0xFFFF; HandleTable[Handle].Status = Session; dLoaderInsertSearchStr((HandleTable[Handle].SearchStr), pFind, &(HandleTable[Handle].SearchType)); Handle = dLoaderFindNext(Handle, pFound, pFileLength, pDataLength); } } return(Handle); } UWORD dLoaderFindNext(UWORD Handle, UBYTE *pFound, ULONG *pFileLength, ULONG *pDataLength) { UBYTE Tmp; UWORD ReturnVal; FILEHEADER *pHeader; *pFileLength = 0; ReturnVal = Handle | FILENOTFOUND; for (Tmp = ((HandleTable[Handle].FileIndex) + 1); Tmp < MAX_FILES; Tmp++) { if (0xFFFFFFFF != FILEPTRTABLE[Tmp]) { if (SUCCESS == dLoaderCheckName((UBYTE*)FILEPTRTABLE[Tmp], HandleTable[Handle].SearchStr, HandleTable[Handle].SearchType)) { HandleTable[Handle].FileIndex = Tmp; Tmp = MAX_FILES; ReturnVal = Handle; } } } if (0x8000 > ReturnVal) { pHeader = (FILEHEADER *)FILEPTRTABLE[HandleTable[Handle].FileIndex]; if (NULL != pFileLength) { *pFileLength = pHeader->FileSize; } if (NULL != pDataLength) { *pDataLength = pHeader->DataSize; } if (NULL != pFound) { dLoaderCopyFileName(pFound, (UBYTE *)pHeader->FileName); } } return(ReturnVal); } ULONG dLoaderReturnFreeFlash(void) { ULONG SectorCnt, IndexPtr; UWORD Sectors; Sectors = 0; IndexPtr = (ULONG)0x01 << SECTORPOINTERUSERFLASH; /* Offset in first index can be different from 0 */ for(SectorCnt = SECTORINDEXUSERFLASH; SectorCnt <= ((NOOFSECTORS>>5)-1); SectorCnt++) { for( ; IndexPtr > 0; IndexPtr<<=1) { if (!(SectorTable[SectorCnt] & IndexPtr)) { Sectors++; } } IndexPtr = 0x00000001; } FreeSectors = Sectors; return(dLoaderCalcFreeFileSpace(Sectors)); } ULONG dLoaderCalcFreeFileSpace(UWORD NosOfFreeSectors) { UWORD SectorCnt; ULONG Space = 0; ULONG HeaderSpace; /* Calculate only if any sectors available */ if (NosOfFreeSectors) { Space = (ULONG)NosOfFreeSectors << SECTORSIZESHIFT; /* (FreeSectors - 1) is beacuse the the first sector of a file do not */ /* require an entry in the sector table - it is pointed to by the filepointer */ /* in the file pointer table*/ SectorCnt = NosOfFreeSectors - 1; /* If more that one sector is used for the header the first filebody sector do not */ /* require an entry in the sectortable - it is pointed to by the file startpointer */ /* in the file header */ if ((((SectorCnt<<1) + HEADERFIXEDSIZE) & (SECTORSIZE - 1)) < 4) { SectorCnt--; } HeaderSpace = (HEADERFIXEDSIZE + (SectorCnt << 1)); if (HeaderSpace & 0x0003) { /* Header size is not a multiplum of 4 - now make it a mul 4 */ HeaderSpace += (0x0004 - (HeaderSpace & 0x0003)); } Space -= HeaderSpace; } return(Space); } UWORD dLoaderGetFilePtr(UBYTE *pFileName, UBYTE *pPtrToFile, ULONG *pFileLength) { UWORD RtnVal; UBYTE FoundFile[16]; FILEHEADER *File; ULONG DataLength; RtnVal = dLoaderFind(pFileName, FoundFile, pFileLength, &DataLength, (UBYTE)BUSY); if (0x8000 > RtnVal) { File = (FILEHEADER*) FILEPTRTABLE[HandleTable[RtnVal].FileIndex]; if (LINEAR & File->FileType) { *((ULONG*)pPtrToFile) = File->FileStartAdr; } else { RtnVal |= NOTLINEARFILE; } } return(RtnVal); } UWORD dLoaderAllocateHeader(UWORD Handle, ULONG *FileStartAdr, FILEHEADER *pHeader, UWORD HeaderByteSize, UWORD CompleteFileSectorSize) { UWORD Tmp; UWORD SectorTableIndex; ULONG SectorIndex; UWORD HeaderSectorSize; UBYTE EvenHeader; UWORD FileBodySectorSize; UWORD ErrorCode; HeaderSectorSize = ((HeaderByteSize - 1) >> SECTORSIZESHIFT) + 1; FileBodySectorSize = (((pHeader->FileSize - (SECTORSIZE - (HeaderByteSize & (SECTORSIZE - 1)))) - 1) >> SECTORSIZESHIFT) + 1; /* First allocate the file file header - this means the file name, */ /* the file start adress, and the sector table */ /* SectorTableIndex indicates in the last word of a sector in which */ /* sector the sectortable continues */ SectorTableIndex = ((SECTORSIZE - HEADERFIXEDSIZE)>>1) - 1; /* Find first free sector - here there is a differende between linear or not*/ ErrorCode = dLoaderFindFirstSector(pHeader->FileType, CompleteFileSectorSize, &Tmp); if (SUCCESS == ErrorCode) { *FileStartAdr = (ULONG)((ULONG)Tmp << SECTORSIZESHIFT) | FLASHOFFSET; HandleTable[Handle].FileDlPtr = *FileStartAdr; SectorIndex = (Tmp >> 5); Tmp &= 0x1F; SectorTable[SectorIndex]|= (0x01<FileStartAdr = (ULONG)(*FileStartAdr) + HeaderByteSize; /* if there is a sectortable it always starts right after the fixed header (Name + start + size)*/ HandleTable[Handle].pSectorNo = (const UWORD *)(*FileStartAdr + HEADERFIXEDSIZE); /* First header has been allocated by find first function */ HeaderSectorSize--; UWORD TmpHSS = HeaderSectorSize; /* Next part is only executed when more than one sector is used */ if (HeaderSectorSize) { UBYTE ExitCode = FALSE; while ((FALSE == ExitCode) && (SectorIndex < (NOOFSECTORS/32))) { for(; ((Tmp < 32) && (ExitCode == FALSE)); Tmp++) { if (!(SectorTable[SectorIndex] & (0x01<FileSectorTable[SectorTableIndex] = (SectorIndex << 5) + Tmp; SectorTableIndex += (SECTORSIZE/2); HeaderSectorSize--; if (0 == HeaderSectorSize) { pHeader->FileStartAdr = (((SectorIndex << 5) + Tmp) << SECTORSIZESHIFT) + (HeaderByteSize - (TmpHSS<= (SECTORSIZE - 2)) || (0 == (HeaderByteSize & (SECTORSIZE - 1)))) { /* The header uses exact one or several sectors */ /* meaning that the next sector do not go into */ /* the sectortable as it is pointed to by the */ /* FileStart pointer */ EvenHeader = TRUE; } /* Now allocated the file body */ SectorTableIndex = 0; while ((FileBodySectorSize > 0) && (SectorIndex < (NOOFSECTORS/32))) { for(; Tmp < 32; Tmp++) { if (!(SectorTable[SectorIndex] & (0x01<FileStartAdr = (((SectorIndex << 5) + Tmp) << SECTORSIZESHIFT) | FLASHOFFSET; EvenHeader = FALSE; } else { /* Sector is free you can have this one */ SectorTable[SectorIndex] |= (0x01<>1)-1) == SectorTableIndex) || (((SectorTableIndex - ((SECTORSIZE - HEADERFIXEDSIZE)>>1)) & 0x7F) == 127)) { SectorTableIndex++; } pHeader->FileSectorTable[SectorTableIndex] = (SectorIndex << 5) + Tmp; SectorTableIndex++; FileBodySectorSize--; if (0 == FileBodySectorSize) { Tmp = 32; SectorIndex = (NOOFSECTORS/32); } } } } SectorIndex++; Tmp = 0; } } else { Handle |= ErrorCode; } return(Handle); } UWORD dLoaderFindFirstSector(UBYTE Type, UWORD SectorCount, UWORD *pSector) { UWORD CompleteSectorSize; UWORD SectorIndex; UBYTE Tmp; UWORD SectorCnt; UWORD ErrorCode; ErrorCode = SUCCESS; SectorIndex = SECTORINDEXUSERFLASH; Tmp = SECTORPOINTERUSERFLASH; if (LINEAR & Type) { CompleteSectorSize = SectorCount; ErrorCode = NOLINEARSPACE; /* find linear adress space */ SectorCnt = CompleteSectorSize; while ((SectorCnt > 0) && (SectorIndex < (NOOFSECTORS>>5))) { if ((SectorTable[SectorIndex]) & ((ULONG)0x01<>5); ErrorCode = SUCCESS; } } if (0x1F == Tmp) { SectorIndex++; } Tmp = (Tmp + 1) & 0x1F; } } else { ErrorCode = UNDEFINEDERROR; while(SectorIndex < (NOOFSECTORS>>5)) { if (!((SectorTable[SectorIndex]) & ((ULONG)0x01<>5); ErrorCode = SUCCESS; } if (0x1F == Tmp) { SectorIndex++; } Tmp = (Tmp + 1) & 0x1F; } } return(ErrorCode); } UWORD dLoaderFlashFileHeader(UWORD Handle, ULONG FileStartAdr, FILEHEADER *pHeader, UWORD HeaderByteSize) { ULONG *pBufPtr; ULONG FlashPtr; UWORD HeaderSectorSize; pBufPtr = (ULONG*)pHeader; FlashPtr = FileStartAdr; HeaderSectorSize = (HeaderByteSize - 1) >> SECTORSIZESHIFT; dLoaderWritePage(FlashPtr, SECTORSIZE, pBufPtr); while(HeaderSectorSize) { pBufPtr += (SECTORSIZE>>2); FlashPtr = (((*(pBufPtr - 1) & 0xFFFF0000) >> 16) << SECTORSIZESHIFT) | FLASHOFFSET; dLoaderWritePage(FlashPtr, SECTORSIZE, pBufPtr); HeaderSectorSize--; } /* Prepare for actual data download */ memcpy(WriteBuffer[HandleTable[Handle].WriteBufNo].Buf, pBufPtr, SECTORSIZE); WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex = (UWORD)(pHeader->FileStartAdr) & (SECTORSIZE-1); HandleTable[Handle].pFlash = (UBYTE *)pHeader->FileStartAdr; return(Handle); } UWORD dLoaderGetSectorNumber(ULONG Adr) { UWORD SectorNo; SectorNo = (Adr & ~FLASHOFFSET)>>SECTORSIZESHIFT; return(SectorNo); } UWORD dLoaderAllocateWriteBuffer(UWORD Handle) { UBYTE Tmp; UWORD ErrorCode; ErrorCode = NOWRITEBUFFERS; for (Tmp = 0; Tmp < MAX_WRITE_BUFFERS; Tmp++) { if (FREE == WriteBuffer[Tmp].Status) { WriteBuffer[Tmp].Status = BUSY; memset(WriteBuffer[Tmp].Buf, 0xFF, sizeof(WriteBuffer[Tmp].Buf)); WriteBuffer[Tmp].BufIndex = 0; HandleTable[Handle].WriteBufNo = Tmp; ErrorCode = SUCCESS; Tmp = MAX_WRITE_BUFFERS; } } Handle |= ErrorCode; return(Handle); } UWORD dLoaderCheckFiles(UBYTE Handle) { UBYTE Tmp; UBYTE Index; UWORD ErrorCode; ErrorCode = SUCCESS; Index = HandleTable[Handle].FileIndex; for (Tmp = 0; Tmp < MAX_HANDLES; Tmp++) { if (((BUSY == HandleTable[Tmp].Status) || (DOWNLOADING == HandleTable[Tmp].Status)) && (Index == HandleTable[Tmp].FileIndex) && (Tmp != Handle)) { ErrorCode = FILEISBUSY; } } return(Handle | ErrorCode); } void dLoaderCopyFileName(UBYTE *pDst, UBYTE *pSrc) { UBYTE Tmp; for(Tmp = 0; Tmp < FILENAME_SIZE; Tmp++, pDst++) { if ('\0' != *pSrc) { *pDst = *pSrc; pSrc++; } else { *pDst = '\0'; } } } void dLoaderCheckVersion(void) { if (FILEPTRTABLE[FILEVERSIONINDEX] != FILEVERSION) { dLoaderDeleteAllFiles(); } } UWORD dLoaderOpenAppend(UBYTE *pFileName, ULONG *pAvailSize) { UWORD Handle; ULONG FileSize, DataSize; UBYTE Name[FILENAME_SIZE]; FILEHEADER *pHeader; *pAvailSize = 0; Handle = dLoaderFind(pFileName, Name, &FileSize, &DataSize, (UBYTE)BUSY); if (0x8000 > Handle) { /* Check for an append in progress for this file */ if (0x8000 > dLoaderCheckDownload(pFileName)) { /* File has bee found - check for then correct filetype (Datafile) */ pHeader = (FILEHEADER *)FILEPTRTABLE[HandleTable[Handle].FileIndex]; if (DATAFILE & pHeader->FileType) { if (FileSize > DataSize) { /* Append is possible */ Handle = dLoaderAllocateWriteBuffer(Handle); if (Handle < 0x8000) { dLoaderSetFilePointer(Handle, DataSize, &(HandleTable[Handle].pFlash)); WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex = (ULONG)(HandleTable[Handle].pFlash) & (SECTORSIZE - 1); memcpy(WriteBuffer[HandleTable[Handle].WriteBufNo].Buf, (const UBYTE *)((ULONG)(HandleTable[Handle].pFlash) & ~(SECTORSIZE - 1)), WriteBuffer[HandleTable[Handle].WriteBufNo].BufIndex ); HandleTable[Handle].FileDlPtr = FILEPTRTABLE[HandleTable[Handle].FileIndex]; HandleTable[Handle].Status = (UBYTE)DOWNLOADING; *pAvailSize = FileSize - DataSize; HandleTable[Handle].DataLength = *pAvailSize; HandleTable[Handle].FileType = pHeader->FileType; } } else { Handle |= FILEISFULL; } } else { Handle |= APPENDNOTPOSSIBLE; } } else { Handle |= FILEISBUSY; } } return(Handle); } UWORD dLoaderSetFilePointer(UWORD Handle, ULONG BytePtr, const UBYTE **pData) { ULONG AdrOffset; const UBYTE *Adr; UWORD SectorNo; UWORD Tmp; FILEHEADER *pHeader; pData = pData; pHeader = (FILEHEADER*)FILEPTRTABLE[HandleTable[Handle].FileIndex]; HandleTable[Handle].pSectorNo = pHeader->FileSectorTable; /* Get the sector offset */ AdrOffset = SECTORSIZE - ((pHeader->FileStartAdr) & (SECTORSIZE - 1)); if (BytePtr > AdrOffset) { BytePtr = BytePtr - AdrOffset; SectorNo = ((BytePtr >> SECTORSIZESHIFT) + 1); for (Tmp = 0; Tmp < SectorNo; Tmp++) { Adr = dLoaderGetNextSectorPtr(Handle); if (BytePtr > SECTORSIZE) { BytePtr -= SECTORSIZE; } } *pData = (const UBYTE *)((ULONG)Adr + BytePtr); } else { /* Pointer reside in the first sector of the file body */ *pData = (const UBYTE *)((ULONG)(pHeader->FileStartAdr) + BytePtr); } return(Handle); } void dLoaderCpyToLower(UBYTE *pDst, UBYTE *pSrc, UBYTE Length) { UBYTE Tmp; for(Tmp = 0; Tmp < Length; Tmp++) { pDst[Tmp] =(UBYTE)toupper((UWORD)pSrc[Tmp]); } /* The requried length has been copied - now fill with zeros */ for(Tmp = Length; Tmp < FILENAME_SIZE; Tmp++) { pDst[Tmp] = '\0'; } } UWORD dLoaderCheckName(UBYTE *pName, UBYTE *pSearchStr, UBYTE SearchType) { UBYTE TmpName[FILENAME_SIZE]; UWORD RtnVal; RtnVal = UNDEFINEDERROR; dLoaderCpyToLower(TmpName, pName, (UBYTE)FILENAME_SIZE); RtnVal = SUCCESS; switch (SearchType) { case FULLNAME: { if (0 != strcmp((const char*)TmpName, (const char *)pSearchStr)) { RtnVal = UNDEFINEDERROR; } } break; case NAME: { if (0 != memcmp(TmpName, pSearchStr, strlen((const char *)pSearchStr))) { RtnVal = UNDEFINEDERROR; } } break; case EXTENTION: { if (0 == strstr((const char *)TmpName, (const char*)pSearchStr)) { RtnVal = UNDEFINEDERROR; } } break; case WILDCARD: { RtnVal = SUCCESS; } break; default: { } break; } return(RtnVal); } void dLoaderInsertSearchStr(UBYTE *pDst, UBYTE *pSrc, UBYTE *pSearchType) { UBYTE Tmp; *pSearchType = WILDCARD; if (0 != strstr((char const *)pSrc, "*.*")) { /* find all */ strcpy ((PSZ)pDst, (PSZ)pSrc); *pSearchType = WILDCARD; } else { /* Using other wild cards? */ Tmp = strlen((char const *)pSrc); if (0 != strstr((PSZ)(pSrc), ".*")) { /* Extention wildcard */ dLoaderCpyToLower(pDst, pSrc, (Tmp-1)); *pSearchType = NAME; } else { if (0 != strstr((PSZ)(pSrc), "*.")) { /* Filename wildcard */ dLoaderCpyToLower(pDst, &pSrc[1], (UBYTE)4); *pSearchType = EXTENTION; } else { /* no wildcards used */ dLoaderCpyToLower(pDst, pSrc, Tmp); *pSearchType = FULLNAME; } } } } UWORD dLoaderCheckHandle(UWORD Handle, UBYTE Operation) { if (MAX_HANDLES > Handle) { if (Operation != HandleTable[(UBYTE)Handle].Status) { Handle |= ILLEGALHANDLE; } } else { Handle |= ILLEGALHANDLE; } return(Handle); } ULONG dLoaderReturnFreeUserFlash(void) { return(FreeUserFlash); } UWORD dLoaderRenameFile(UBYTE Handle, UBYTE *pNewName) { ULONG SectorBuf[SECTORSIZE/4]; ULONG *pFile; UBYTE Tmp; FILEHEADER *pHeader; pFile = (ULONG *)FILEPTRTABLE[HandleTable[Handle].FileIndex]; for (Tmp = 0; Tmp < (SECTORSIZE/4); Tmp++) { SectorBuf[Tmp] = pFile[Tmp]; } pHeader = (FILEHEADER *) SectorBuf; dLoaderCopyFileName((pHeader->FileName), pNewName); dLoaderWritePage((ULONG)pFile, SECTORSIZE, SectorBuf); return(SUCCESS); } UWORD dLoaderCheckDownload(UBYTE *pName) { UBYTE Tmp; UWORD ErrorCode; ErrorCode = SUCCESS; for(Tmp = 0; Tmp < MAX_HANDLES; Tmp ++) { if (DOWNLOADING == HandleTable[Tmp].Status) { if (SUCCESS == dLoaderCheckName(pName, HandleTable[Tmp].SearchStr, FULLNAME)) { Tmp = MAX_HANDLES; ErrorCode = FILEEXISTS; } } } return(ErrorCode); } UWORD dLoaderCropDatafile(UBYTE Handle) { UWORD ReturnVal; ULONG SectorBuffer[SECTORSIZE]; UBYTE FileIndex; /* Save the fileindex for use after the handle has been closed */ FileIndex = HandleTable[Handle].FileIndex; ReturnVal = dLoaderCloseHandle(Handle); if (0x8000 > ReturnVal) { /* Successful close handle now try to crop the file if filesize and datasize differs */ /* and File exists */ if (((FILEPTRTABLE[FileIndex]) != 0x00000000) && ((FILEPTRTABLE[FileIndex]) != 0xFFFFFFFF)) { if (((FILEHEADER const *)(FILEPTRTABLE[FileIndex]))->FileSize != ((FILEHEADER const *)(FILEPTRTABLE[FileIndex]))->DataSize) { memcpy(SectorBuffer, (void const*)(FILEPTRTABLE[FileIndex]), SECTORSIZE); ((FILEHEADER*)SectorBuffer)->FileSize = ((FILEHEADER const *)(FILEPTRTABLE[FileIndex]))->DataSize; dLoaderWritePage((ULONG)(FILEPTRTABLE[HandleTable[Handle].FileIndex]), SECTORSIZE, SectorBuffer); /* Update sectortable and available flash size */ dLoaderUpdateSectorTable(); } } } return(ReturnVal); } void dLoaderExit(void) { } nxt-firmware-1.29.7/src/d_loader.h000066400000000000000000000072551466344546000167730ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 24-06-09 12:15 $ // // Filename $Workfile:: d_loader.h $ // // Version $Revision:: 18 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_load $ // // Platform C // #ifndef D_LOADER #define D_LOADER #define FILETABLE_SIZE ((2 * SECTORSIZE)/4) #define STARTOFFILETABLE (0x140000L - (FILETABLE_SIZE*4)) #define FILEPTRTABLE ((const ULONG*)(0x140000L - (FILETABLE_SIZE*4))) #ifndef STARTOFUSERFLASH_FROM_LINKER #define STARTOFUSERFLASH (0x122100L) #define SIZEOFUSERFLASH_MAX SIZEOFUSERFLASH #else extern char __STARTOFUSERFLASH_FROM_LINKER; #define STARTOFUSERFLASH ((ULONG) &__STARTOFUSERFLASH_FROM_LINKER) #define SIZEOFUSERFLASH_MAX ((ULONG) (128 * 1024)) #endif #define SIZEOFUSERFLASH ((ULONG)STARTOFFILETABLE - STARTOFUSERFLASH) #define SIZEOFFLASH 262144L #define SECTORSIZE 256L #define SECTORSIZESHIFT 8 #define NOOFSECTORS (SIZEOFFLASH/SECTORSIZE) #define HEADERFIXEDSIZE (FILENAME_SIZE + 4 + 4 + 4 + 2 + 2) #define FILENAME_SIZE (FILENAME_LENGTH + 1) #define FULLNAME 1 #define NAME 2 #define EXTENTION 3 #define WILDCARD 4 /* Enum related to HandleTable Status */ enum { FREE, BUSY, DOWNLOADING, SEARCHING, DLERROR }; /* Enum related to HandleTable WriteBufNo */ enum { FREEBUFNO = 0xFF }; /* Constants related to filetype */ enum { SYSTEMFILE = 0x01, DATAFILE = 0x02, LINEAR = 0x04, NONLINEAR = 0x08 }; typedef struct { UBYTE FileName[FILENAME_SIZE]; ULONG FileStartAdr; ULONG FileSize; ULONG DataSize; UWORD CheckSum; UWORD FileType; UWORD FileSectorTable[(SIZEOFUSERFLASH_MAX/SECTORSIZE)]; }FILEHEADER; void dLoaderInit(void); __ramfunc UWORD dLoaderWritePage(ULONG Flash_Address, UWORD Size, ULONG *pBuf); UWORD dLoaderInsertPtrTable(const UBYTE *pAdr, UWORD Handle); UWORD dLoaderCreateFileHeader(ULONG FileSize, UBYTE *pName, UBYTE LinearState, UBYTE FileType); UWORD dLoaderWriteData(UWORD Handle, UBYTE *pBuf, UWORD *pLen); UWORD dLoaderCloseHandle(UWORD Handle); UWORD dLoaderOpenRead(UBYTE *pFileName, ULONG *pLength); UWORD dLoaderRead(UBYTE Handle, UBYTE *pBuf, ULONG *pLength); UWORD dLoaderDelete(UBYTE *pFile); UWORD dLoaderFind(UBYTE *pFind, UBYTE *pFound, ULONG *pFileLength, ULONG *pDataLength, UBYTE Session); UWORD dLoaderFindNext(UWORD Handle, UBYTE *pFound, ULONG *pFileLength, ULONG *pDataLength); UWORD dLoaderDeleteFilePtr(UWORD Handle); void dLoaderDeleteAllFiles(void); UWORD dLoaderGetFilePtr(UBYTE *pFileName, UBYTE *pPtrToFile, ULONG *pFileLength); void dLoaderCopyFileName(UBYTE *pDst, UBYTE *pSrc); UWORD dLoaderOpenAppend(UBYTE *pFileName, ULONG *pAvailSize); void dLoaderCpyToLower(UBYTE *pDst, UBYTE *pSrc, UBYTE Length); UWORD dLoaderCheckName(UBYTE *pName, UBYTE *pSearchStr, UBYTE SearchType); void dLoaderInsertSearchStr(UBYTE *pDst, UBYTE *pSrc, UBYTE *pSearchType); ULONG dLoaderReturnFreeUserFlash(void); UWORD dLoaderRenameFile(UBYTE Handle, UBYTE *pNewName); UWORD dLoaderCheckFiles(UBYTE Handle); UWORD dLoaderCropDatafile(UBYTE Handle); void dLoaderExit(void); #endif nxt-firmware-1.29.7/src/d_loader.r000066400000000000000000000054311466344546000167770ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_loader.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_load $ // // Platform C // #ifdef SAM7S256 #define AT91C_MC_CORRECT_KEY 0x5A000000L static ULONG SectorImage[SECTORSIZE>>2]; #define LOADERInit __ramfunc UWORD AT91F_Flash_Ready (void) { UWORD status; status = 0; //* Wait the end of command while ((status & AT91C_MC_FRDY) != AT91C_MC_FRDY ) { status = AT91C_BASE_MC->MC_FSR; } return status; } __ramfunc UWORD dLoaderWritePage(ULONG Flash_Address, UWORD Size, ULONG *pBuf) { //* set the Flash controller base address AT91PS_MC ptMC = AT91C_BASE_MC; unsigned int i, page, status; unsigned int * Flash; //* init flash pointer Flash = (unsigned int *) (Flash_Address | (unsigned int)AT91C_IFLASH); //* Get the Flash page number page = ((Flash_Address & ~(unsigned int)AT91C_IFLASH) >> SECTORSIZESHIFT); //* copy the new value if (Size & 0x0003) { Size = Size + (0x0004 - (Size & 0x0003)); } for (i=0; (i < SECTORSIZE) & (Size > 0) ;i++, Flash++,pBuf++,Size-=4 ) { //* copy the flash to the write buffer ensuring code generation *Flash=*pBuf; } //* Write the write page command ptMC->MC_FCR = AT91C_MC_CORRECT_KEY | AT91C_MC_FCMD_START_PROG | (AT91C_MC_PAGEN & (page <<8)); //* Wait the end of command status = AT91F_Flash_Ready(); //* Check the result if ( (status & ( AT91C_MC_PROGE | AT91C_MC_LOCKE ))!=0) { return FALSE; } return TRUE; } __ramfunc UWORD dLoaderErasePage(ULONG Flash_Address) { //* set the Flash controller base address AT91PS_MC ptMC = AT91C_BASE_MC; unsigned int i, page, status, Size; unsigned int * Flash; Size = SECTORSIZE; //* init flash pointer Flash = (unsigned int *) (Flash_Address | (unsigned int)AT91C_IFLASH); //* Get the Flash page number page = ((Flash_Address & ~(unsigned int)AT91C_IFLASH) >> SECTORSIZESHIFT); //* copy the new value for (i=0; (i < SECTORSIZE) & (Size > 0) ;i++, Flash++,Size-=4 ) { //* copy the flash to the write buffer ensuring code generation *Flash=0xFFFFFFFF; } //* Write the write page command ptMC->MC_FCR = AT91C_MC_CORRECT_KEY | AT91C_MC_FCMD_START_PROG | (AT91C_MC_PAGEN & (page <<8)); //* Wait the end of command status = AT91F_Flash_Ready(); //* Check the result if ( (status & ( AT91C_MC_PROGE | AT91C_MC_LOCKE ))!=0) { return FALSE; } return TRUE; } #endif nxt-firmware-1.29.7/src/d_lowspeed.c000066400000000000000000000027461466344546000173420ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_lowspeed.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_lows $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_lowspeed.h" #include "d_lowspeed.r" void dLowSpeedInit(void) { LOWSpeedTxInit; LOWSpeedTimerInit; //ENABLEDebugOutput; } void dLowSpeedStartTimer(void) { ENABLEPWMTimerForLowCom; } void dLowSpeedStopTimer(void) { DISABLEPWMTimerForLowCom; } void dLowSpeedInitPins(UBYTE ChannelNumber) { ENABLETxPins(ChannelNumber); } UBYTE dLowSpeedSendData(UBYTE ChannelNumber, UBYTE *DataOutBuffer, UBYTE NumberOfTxByte) { UBYTE Status; TxData(ChannelNumber, Status, DataOutBuffer, NumberOfTxByte); return(Status); } void dLowSpeedReceiveData(UBYTE ChannelNumber, UBYTE *DataInBuffer, UBYTE ByteToRx) { RxData(ChannelNumber, DataInBuffer, ByteToRx); } UBYTE dLowSpeedComTxStatus(UBYTE ChannelNumber) { UBYTE Status; STATUSTxCom(ChannelNumber, Status) return(Status); } UBYTE dLowSpeedComRxStatus(UBYTE ChannelNumber) { UBYTE Status; STATUSRxCom(ChannelNumber, Status) return(Status); } void dLowSpeedExit(void) { LOWSpeedExit; } nxt-firmware-1.29.7/src/d_lowspeed.h000066400000000000000000000016551466344546000173450ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_lowspeed.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_lows $ // // Platform C // #ifndef D_LOWSPEED #define D_LOWSPEED void dLowSpeedInit(void); void dLowSpeedStartTimer(void); void dLowSpeedStopTimer(void); void dLowSpeedInitPins(UBYTE ChannelNumber); UBYTE dLowSpeedSendData(UBYTE ChannelNumber, UBYTE *DataOutBuffer, UBYTE NumberOfTxByte); void dLowSpeedReceiveData(UBYTE ChannelNumber, UBYTE *DataInBuffer, UBYTE ByteToRx); UBYTE dLowSpeedComTxStatus(UBYTE ChannelNumber); UBYTE dLowSpeedComRxStatus(UBYTE ChannelNumber); void dLowSpeedExit(void); #endif nxt-firmware-1.29.7/src/d_lowspeed.r000066400000000000000000000524071466344546000173600ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 19-02-09 18:51 $ // // Filename $Workfile:: d_lowspeed.r $ // // Version $Revision:: 4 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_lows $ // // Platform C // #ifdef SAM7S256 #if defined (PROTOTYPE_PCB_3) || (PROTOTYPE_PCB_4) #define CHANNEL_ONE_CLK AT91C_PIO_PA23 /* PA23 is Clk */ #define CHANNEL_ONE_DATA AT91C_PIO_PA18 /* PA18 is Data */ #define CHANNEL_TWO_CLK AT91C_PIO_PA28 /* PA28 is Clk */ #define CHANNEL_TWO_DATA AT91C_PIO_PA19 /* PA19 is Data */ #define CHANNEL_THREE_CLK AT91C_PIO_PA29 /* PA29 is Clk */ #define CHANNEL_THREE_DATA AT91C_PIO_PA20 /* PA20 is Data */ #define CHANNEL_FOUR_CLK AT91C_PIO_PA30 /* PA30 is Clk */ #define CHANNEL_FOUR_DATA AT91C_PIO_PA2 /* PA2 is Data */ #else #define CHANNEL_ONE_CLK AT91C_PIO_PA28 /* PA28 is Clk */ #define CHANNEL_ONE_DATA AT91C_PIO_PA20 /* PA20 is Data */ #endif typedef struct { UWORD MaskBit; UBYTE ChannelState; UBYTE TxState; UBYTE RxState; UBYTE ReStartState; UBYTE TxByteCnt; UBYTE RxByteCnt; UBYTE *pComOutBuffer; UBYTE *pComInBuffer; UBYTE AckStatus; UBYTE RxBitCnt; UBYTE ReStartBit; UBYTE ComDeviceAddress; UBYTE RxWaitCnt; UBYTE ClkStatus; }LOWSPEEDPARAMETERS; static LOWSPEEDPARAMETERS LowSpeedData[4]; __ramdata ULONG DATA_PINS[4] = {CHANNEL_ONE_DATA, CHANNEL_TWO_DATA, CHANNEL_THREE_DATA, CHANNEL_FOUR_DATA}; __ramdata ULONG CLK_PINS[4] = {CHANNEL_ONE_CLK, CHANNEL_TWO_CLK, CHANNEL_THREE_CLK, CHANNEL_FOUR_CLK}; #define LOWSPEED_CHANNEL1 0 #define LOWSPEED_CHANNEL2 1 #define LOWSPEED_CHANNEL3 2 #define LOWSPEED_CHANNEL4 3 #define NO_OF_LOWSPEED_COM_CHANNEL 4 #define MASK_BIT_8 0x80 #define PIO_INQ 0x04 //Used for variable ChannelState #define LOWSPEED_IDLE 0x00 #define LOWSPEED_TX_STOP_BIT 0x01 #define LOWSPEED_TRANSMITTING 0x02 #define LOWSPEED_RECEIVING 0x04 #define LOWSPEED_TEST_WAIT_STATE 0x08 #define LOWSPEED_RESTART_CONDITION 0x10 #define LOWSPEED_WAIT_BEFORE_RX 0x20 //Used for variable TxState #define TX_IDLE 0x00 #define TX_DATA_MORE_DATA 0x01 #define TX_DATA_CLK_HIGH 0x02 #define TX_EVALUATE_ACK_CLK_HIGH 0x03 #define TX_DATA_READ_ACK_CLK_LOW 0x04 #define TX_DATA_CLK_LOW 0x05 #define TX_ACK_EVALUATED_CLK_LOW 0x06 //Used for variable RxState #define RX_IDLE 0x00 #define RX_START_BIT_CLK_HIGH 0x01 #define RX_DATA_CLK_HIGH 0x02 #define RX_ACK_TX_CLK_HIGH 0x03 #define RX_DATA_CLK_LOW 0x04 #define RX_DONE_OR_NOT_CLK_LOW 0x05 //Used for variable ReStart #define RESTART_STATE_IDLE 0x00 #define RESTART_STATE_ONE 0x01 #define RESTART_STATE_TWO 0x02 #define RESTART_STATE_THREE 0x03 #define RESTART_STATE_FOUR 0x04 #define RESTART_STATE_FIVE 0x05 #define RESTART_STATE_SIX 0x06 #define RESTART_STATE_SEVEN 0x07 #define LOWSpeedTxInit {\ LowSpeedData[LOWSPEED_CHANNEL1].ChannelState = 0;\ LowSpeedData[LOWSPEED_CHANNEL2].ChannelState = 0;\ LowSpeedData[LOWSPEED_CHANNEL3].ChannelState = 0;\ LowSpeedData[LOWSPEED_CHANNEL4].ChannelState = 0;\ } #define LOWSpeedTimerInit {\ *AT91C_PMC_PCER = 0x400; /* Enable clock for PWM, PID10*/\ *AT91C_PWMC_MR = 0x01; /* CLKA is output from prescaler */\ *AT91C_PWMC_MR |= 0x600; /* Prescaler MCK divided with 64 */\ *AT91C_PWMC_CH0_CMR = 0x06; /* Channel 0 uses MCK divided by 64 */\ *AT91C_PWMC_CH0_CMR &= 0xFFFFFEFF; /* Left alignment on periode */\ *AT91C_PWMC_CH0_CPRDR = 0x20; /* Set to 39 => 52uSecondes interrupt */\ *AT91C_PWMC_IDR = AT91C_PWMC_CHID0; /* Disable interrupt for PWM output channel 0 */\ *AT91C_AIC_IDCR = 0x400; /* Disable AIC intterupt on ID10 PWM */\ AT91C_AIC_SVR[10] = (unsigned int)LowSpeedPwmIrqHandler;\ AT91C_AIC_SMR[10] = 0x01; /* Enable trigger on level */\ *AT91C_AIC_ICCR = 0x400; /* Clear interrupt register PID10*/\ *AT91C_PWMC_IER = AT91C_PWMC_CHID0; /* Enable interrupt for PWM output channel 0 */\ *AT91C_AIC_IECR = 0x400; /* Enable interrupt from PWM */\ } #define LOWSpeedExit #define ENABLEDebugOutput {\ *AT91C_PIOA_PER = AT91C_PIO_PA29; /* Enable PIO on PA029 */\ *AT91C_PIOA_OER = AT91C_PIO_PA29; /* PA029 set to Output */\ *AT91C_PIOA_CODR = 0x20000000;\ } #define SETDebugOutputHigh *AT91C_PIOA_SODR = 0x20000000 #define SETDebugOutputLow *AT91C_PIOA_CODR = 0x20000000 #define SETClkLow(ChannelNr) {\ *AT91C_PIOA_CODR = CLK_PINS[ChannelNr];\ LowSpeedData[ChannelNr].ClkStatus = 0;\ } #define SETClkHigh(ChannelNr) {\ *AT91C_PIOA_SODR = CLK_PINS[ChannelNr];\ LowSpeedData[ChannelNr].ClkStatus = 1;\ } #define SETDataLow(ChannelNr) {\ *AT91C_PIOA_CODR = DATA_PINS[ChannelNr];\ } #define SETDataHigh(ChannelNr) {\ *AT91C_PIOA_SODR = DATA_PINS[ChannelNr];\ } #define SETDataToInput(ChannelNr) {\ *AT91C_PIOA_ODR = DATA_PINS[ChannelNr];\ } #define SETDataToOutput(ChannelNr) {\ *AT91C_PIOA_OER = DATA_PINS[ChannelNr];\ } #define GetClkPinLevel(ChannelNr) (*AT91C_PIOA_PDSR & CLK_PINS[ChannelNr]) #define GetDataPinLevel(ChannelNr) (*AT91C_PIOA_PDSR & DATA_PINS[ChannelNr]) #define ENABLEPWMTimerForLowCom {\ *AT91C_PWMC_ENA = AT91C_PWMC_CHID0; /* Enable PWM output channel 0 */\ } #define DISABLEPWMTimerForLowCom {\ *AT91C_PWMC_DIS = AT91C_PWMC_CHID0; /* Disable PWM output channel 0 */\ } #define OLD_DISABLEPWMTimerForLowCom {\ *AT91C_PWMC_DIS = AT91C_PWMC_CHID0; /* Disable PWM output channel 0 */\ *AT91C_PWMC_IDR = AT91C_PWMC_CHID0; /* Disable interrupt from PWM output channel 0 */\ *AT91C_AIC_IDCR = 0x400; /* Disable Irq from PID10 */\ *AT91C_AIC_ICCR = 0x400; /* Clear interrupt register PID10*/\ *AT91C_PMC_PCDR = 0x400; /* Disable clock for PWM, PID10*/\ } __ramfunc void LowSpeedPwmIrqHandler(void) { ULONG TestVar; ULONG PinStatus; UBYTE ChannelNr; TestVar = *AT91C_PWMC_ISR; TestVar = TestVar; PinStatus = *AT91C_PIOA_PDSR; for (ChannelNr = 0; ChannelNr < NO_OF_LOWSPEED_COM_CHANNEL; ChannelNr++) { if (((LowSpeedData[ChannelNr].ClkStatus == 1) && (PinStatus & CLK_PINS[ChannelNr])) || (((LowSpeedData[ChannelNr].ClkStatus == 0) && (!(PinStatus & CLK_PINS[ChannelNr]))))) { switch(LowSpeedData[ChannelNr].ChannelState) { case LOWSPEED_IDLE: { } break; case LOWSPEED_TX_STOP_BIT: { SETDataHigh(ChannelNr); LowSpeedData[ChannelNr].ChannelState = LOWSPEED_IDLE; //Now we have send a STOP sequence, disable this channel } break; case LOWSPEED_TRANSMITTING: { switch(LowSpeedData[ChannelNr].TxState) { case TX_DATA_MORE_DATA: { PinStatus |= CLK_PINS[ChannelNr]; LowSpeedData[ChannelNr].TxState = TX_DATA_CLK_HIGH; } break; case TX_DATA_CLK_HIGH: { SETClkLow(ChannelNr); if (LowSpeedData[ChannelNr].MaskBit == 0) //Is Byte Done, then we need a ack from receiver { SETDataToInput(ChannelNr); //Set datapin to input LowSpeedData[ChannelNr].TxState = TX_DATA_READ_ACK_CLK_LOW; } else { if (*LowSpeedData[ChannelNr].pComOutBuffer & LowSpeedData[ChannelNr].MaskBit) //Setup data pin in relation to the data { SETDataHigh(ChannelNr); //Set data output high } else { SETDataLow(ChannelNr); //Set data output low } LowSpeedData[ChannelNr].TxState = TX_DATA_CLK_LOW; } } break; case TX_EVALUATE_ACK_CLK_HIGH: { SETClkLow(ChannelNr); if (LowSpeedData[ChannelNr].AckStatus == 1) { LowSpeedData[ChannelNr].TxByteCnt--; if (LowSpeedData[ChannelNr].TxByteCnt > 0) //Here initialise to send next byte { LowSpeedData[ChannelNr].MaskBit = MASK_BIT_8; LowSpeedData[ChannelNr].pComOutBuffer++; } LowSpeedData[ChannelNr].TxState = TX_ACK_EVALUATED_CLK_LOW; //Received ack, now make a stop sequence or send next byte } else { //Data communication error ! LowSpeedData[ChannelNr].TxByteCnt = 0; SETClkHigh(ChannelNr); LowSpeedData[ChannelNr].ChannelState = LOWSPEED_TX_STOP_BIT; //Received ack, now make a stop sequence or send next byte. } } break; case TX_DATA_READ_ACK_CLK_LOW: { if (!(PinStatus & DATA_PINS[ChannelNr])) { LowSpeedData[ChannelNr].AckStatus = 1; //Read ack signal from receiver } SETDataToOutput(ChannelNr); SETDataLow(ChannelNr); LowSpeedData[ChannelNr].TxState = TX_EVALUATE_ACK_CLK_HIGH; SETClkHigh(ChannelNr); } break; case TX_DATA_CLK_LOW: { LowSpeedData[ChannelNr].MaskBit = LowSpeedData[ChannelNr].MaskBit >> 1; //Get ready for the next bit which should be clk out next time SETClkHigh(ChannelNr); //Clk goes high = The reciever reads the data LowSpeedData[ChannelNr].TxState = TX_DATA_CLK_HIGH; } break; case TX_ACK_EVALUATED_CLK_LOW: { if (LowSpeedData[ChannelNr].MaskBit != 0) { LowSpeedData[ChannelNr].TxState = TX_DATA_MORE_DATA; } else { if (LowSpeedData[ChannelNr].ReStartBit != 0) { LowSpeedData[ChannelNr].ChannelState = LOWSPEED_RESTART_CONDITION; LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_ONE; SETDataLow(ChannelNr); SETClkHigh(ChannelNr); //Clk goes high = The reciever reads the data } else { if (LowSpeedData[ChannelNr].RxByteCnt != 0) { LowSpeedData[ChannelNr].ChannelState = LOWSPEED_WAIT_BEFORE_RX; } else { LowSpeedData[ChannelNr].ChannelState = LOWSPEED_TX_STOP_BIT; SETClkHigh(ChannelNr); //Clk goes high = The reciever reads the data } } LowSpeedData[ChannelNr].TxState = TX_IDLE; } } break; } } break; case LOWSPEED_RESTART_CONDITION: { switch(LowSpeedData[ChannelNr].ReStartState) { case RESTART_STATE_ONE: { LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_TWO; } break; case RESTART_STATE_TWO: { SETDataHigh(ChannelNr); LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_THREE; } break; case RESTART_STATE_THREE: { SETClkLow(ChannelNr); LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_FOUR; } break; case RESTART_STATE_FOUR: { SETClkHigh(ChannelNr); LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_FIVE; } break; case RESTART_STATE_FIVE: { SETDataLow(ChannelNr); LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_SIX; } break; case RESTART_STATE_SIX: { LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_SEVEN; } break; case RESTART_STATE_SEVEN: { SETClkLow(ChannelNr); LowSpeedData[ChannelNr].ReStartState = RESTART_STATE_IDLE; LowSpeedData[ChannelNr].ReStartBit = 0; LowSpeedData[ChannelNr].pComOutBuffer = &LowSpeedData[ChannelNr].ComDeviceAddress; *LowSpeedData[ChannelNr].pComOutBuffer += 0x01; LowSpeedData[ChannelNr].ChannelState = LOWSPEED_TRANSMITTING; LowSpeedData[ChannelNr].MaskBit = MASK_BIT_8; LowSpeedData[ChannelNr].TxByteCnt = 0x01; LowSpeedData[ChannelNr].TxState = TX_DATA_CLK_HIGH; LowSpeedData[ChannelNr].AckStatus = 0; } break; } } break; case LOWSPEED_WAIT_BEFORE_RX: { LowSpeedData[ChannelNr].RxWaitCnt++; if (LowSpeedData[ChannelNr].RxWaitCnt > 5) { LowSpeedData[ChannelNr].ChannelState = LOWSPEED_RECEIVING; SETDataToInput(ChannelNr); } } break; case LOWSPEED_RECEIVING: { switch(LowSpeedData[ChannelNr].RxState) { case RX_START_BIT_CLK_HIGH: { SETClkLow(ChannelNr); LowSpeedData[ChannelNr].RxState = RX_DATA_CLK_LOW; } break; case RX_DATA_CLK_HIGH: { LowSpeedData[ChannelNr].RxBitCnt++; if(PinStatus & DATA_PINS[ChannelNr]) { *LowSpeedData[ChannelNr].pComInBuffer |= 0x01; } SETClkLow(ChannelNr); if (LowSpeedData[ChannelNr].RxBitCnt < 8) { *LowSpeedData[ChannelNr].pComInBuffer = *LowSpeedData[ChannelNr].pComInBuffer << 1; } else { if (LowSpeedData[ChannelNr].RxByteCnt > 1) { SETDataToOutput(ChannelNr); SETDataLow(ChannelNr); } } LowSpeedData[ChannelNr].RxState = RX_DATA_CLK_LOW; } break; case RX_ACK_TX_CLK_HIGH: { SETClkLow(ChannelNr); SETDataToInput(ChannelNr); LowSpeedData[ChannelNr].pComInBuffer++; LowSpeedData[ChannelNr].RxByteCnt--; LowSpeedData[ChannelNr].RxBitCnt = 0; LowSpeedData[ChannelNr].RxState = RX_DONE_OR_NOT_CLK_LOW; } break; case RX_DATA_CLK_LOW: { SETClkHigh(ChannelNr); if (LowSpeedData[ChannelNr].RxBitCnt == 8) { LowSpeedData[ChannelNr].RxState = RX_ACK_TX_CLK_HIGH; } else { LowSpeedData[ChannelNr].RxState = RX_DATA_CLK_HIGH; } } break; case RX_DONE_OR_NOT_CLK_LOW: { if (LowSpeedData[ChannelNr].RxByteCnt == 0) { LowSpeedData[ChannelNr].ChannelState = LOWSPEED_IDLE; LowSpeedData[ChannelNr].RxState = RX_IDLE; SETClkHigh(ChannelNr); } else { LowSpeedData[ChannelNr].RxState = RX_START_BIT_CLK_HIGH; } } break; } } break; default: break; } } else { if (LOWSPEED_IDLE != LowSpeedData[ChannelNr].ChannelState) { //Data communication error ! LowSpeedData[ChannelNr].TxByteCnt = 0; SETClkHigh(ChannelNr); LowSpeedData[ChannelNr].ChannelState = LOWSPEED_TX_STOP_BIT; } } } } #define ENABLETxPins(ChannelNumber) {\ ULONG Tmp = CLK_PINS[ChannelNumber] | DATA_PINS[ChannelNumber];\ *AT91C_PIOA_PER = Tmp; /* Enable PIO */\ *AT91C_PIOA_PPUDR = Tmp; /* Disable Pull-up resistor */\ *AT91C_PIOA_ODR = Tmp; /* PIO set to Input */\ } #define TxData(ChannelNumber, Status, DataOutBuffer, NumberOfByte) {\ if ((GetDataPinLevel(ChannelNumber) && GetClkPinLevel(ChannelNumber)) && (LowSpeedData[ChannelNumber].ChannelState == LOWSPEED_IDLE))\ {\ ULONG Tmp = CLK_PINS[ChannelNumber] | DATA_PINS[ChannelNumber];\ *AT91C_PIOA_PER = Tmp; /* Enable PIO */\ *AT91C_PIOA_OER = Tmp; /* POI set to Output */\ *AT91C_PIOA_PPUDR = Tmp; /* Disable Pull-up resistor */\ SETClkHigh(ChannelNumber);\ SETDataLow(ChannelNumber);\ LowSpeedData[ChannelNumber].ClkStatus = 1;\ LowSpeedData[ChannelNumber].pComOutBuffer = DataOutBuffer;\ LowSpeedData[ChannelNumber].ComDeviceAddress = *LowSpeedData[ChannelNumber].pComOutBuffer;\ LowSpeedData[ChannelNumber].MaskBit = MASK_BIT_8;\ LowSpeedData[ChannelNumber].TxByteCnt = NumberOfByte;\ LowSpeedData[ChannelNumber].TxState = TX_DATA_CLK_HIGH;\ LowSpeedData[ChannelNumber].AckStatus = 0;\ LowSpeedData[ChannelNumber].ChannelState = LOWSPEED_TRANSMITTING;\ Status = 1;\ }\ else\ {\ Status = 0;\ }\ } #define RxData(ChannelNumber, DataInBuffer, RxBytes) {\ LowSpeedData[ChannelNumber].pComInBuffer = DataInBuffer;\ LowSpeedData[ChannelNumber].RxBitCnt = 0;\ LowSpeedData[ChannelNumber].RxByteCnt = RxBytes;\ LowSpeedData[ChannelNumber].RxState = RX_DATA_CLK_LOW;\ LowSpeedData[ChannelNumber].ReStartBit = 1;\ LowSpeedData[ChannelNumber].RxWaitCnt = 0;\ } #define STATUSTxCom(ChannelNumber, Status) {\ if (LowSpeedData[ChannelNumber].ChannelState != 0)\ {\ if ((LowSpeedData[ChannelNumber].TxByteCnt == 0) && (LowSpeedData[ChannelNumber].ChannelState != LOWSPEED_RESTART_CONDITION))\ {\ if (LowSpeedData[ChannelNumber].MaskBit == 0)\ {\ if (LowSpeedData[ChannelNumber].AckStatus == 1)\ {\ Status = 0x01; /* TX SUCCESS */\ }\ else\ {\ Status = 0xFF; /* TX ERROR */\ }\ }\ else\ {\ Status = 0;\ }\ }\ else\ {\ Status = 0;\ }\ }\ else\ {\ if (LowSpeedData[ChannelNumber].RxByteCnt == 0)\ {\ if (LowSpeedData[ChannelNumber].AckStatus == 1)\ {\ Status = 0x01; /* TX SUCCESS */\ }\ else\ {\ Status = 0xFF; /* TX ERROR */\ }\ }\ else\ {\ Status = 0xFF; /* TX ERROR */\ }\ }\ } #define STATUSRxCom(ChannelNumber, Status) {\ if (LowSpeedData[ChannelNumber].ChannelState == LOWSPEED_IDLE)\ {\ if (LowSpeedData[ChannelNumber].RxByteCnt == 0)\ {\ Status = 0x01; /* RX SUCCESS */\ }\ else\ {\ Status = 0xFF; /* RX ERROR */\ }\ }\ else\ {\ Status = 0;\ }\ } #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/d_output.c000066400000000000000000001253121466344546000170530ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 3-02-09 14:46 $ // // Filename $Workfile:: d_output.c $ // // Version $Revision:: 2 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_outp $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_output.h" #include "d_output.r" #include #define MAXIMUM_SPEED_FW 100 #define MAXIMUM_SPEED_RW -100 #define INPUT_SCALE_FACTOR 100 #define SPEED_TIME 100 #define MAX_COUNT_TO_RUN 10000000 #define REG_MAX_VALUE 100 #define RAMP_TIME_INTERVAL 25 // Measured in 1 mS => 25 mS interval #define RAMPDOWN_STATE_RAMPDOWN 0 #define RAMPDOWN_STATE_CONTINIUE 1 #define COAST_MOTOR_MODE 0 void dOutputRampDownSynch(UBYTE MotorNr); SLONG dOutputBound(SLONG In, SLONG Limit); SLONG dOutputPIDRegulation(UBYTE MotorNr, SLONG PositionError); SLONG dOutputFractionalChange(SLONG Value, SWORD *FracError); void dOutputSpeedFilter(UBYTE MotorNr, SLONG PositionDiff); #define ABS(a) (((a) < 0) ? -(a) : (a)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) typedef struct { SBYTE MotorSetSpeed; // Motor setpoint in speed SBYTE MotorTargetSpeed; // Speed order for the movement SBYTE MotorActualSpeed; // Actual speed for motor (Calculated within the PID regulation) SBYTE TurnParameter; // Tell the turning parameter used UBYTE RegPParameter; // Current P parameter used within the regulation UBYTE RegIParameter; // Current I parameter used within the regulation UBYTE RegDParameter; // Current D parameter used within the regulation UBYTE RegulationTimeCount; // Time counter used to evaluate when the regulation should run again (100 mS) UBYTE MotorRunState; // Hold current motor state (Ramp-up, Running, Ramp-Down, Idle) UBYTE RegulationMode; // Hold current regulation mode (Position control, Synchronization mode) UBYTE MotorOverloaded; // Set if the motor speed in regulation is calculated to be above maximum UBYTE MotorRunForever; // Tell that the motor is set to run forever UWORD MotorRampDownCount; // Counter to tell if the ramp-down can reach it gaol and therefor need some additional help SWORD MotorRampDownIncrement; // Tell the number of count between each speed adjustment during Ramp-Down UWORD MotorRampUpCount; // Used to speedup Ramp-Up if position regulation is not enabled SWORD MotorRampUpIncrement; // Tell the number of count between each speed adjustment during Ramp-up SWORD AccError; // Accumulated Error, used within the integrator of the PID regulation SWORD OldPositionError; // Used within position regulation SWORD PositionFracError; // Fractionnal position error of last position update SLONG DeltaCaptureCount; // Counts within last regulation time-periode SLONG CurrentCaptureCount; // Total counts since motor counts has been reset SLONG MotorTachoCountToRun; // Holds number of counts to run. 0 = Run forever SLONG MotorBlockTachoCount; // Hold CaptureCount for current movement SLONG MotorRampTachoCountOld; // Used to hold old position during Ramp-Up SLONG MotorRampTachoCountStart; // Used to hold position when Ramp-up started SLONG RotationCaptureCount; // Counter for additional rotation counter SLONG MotorTachoCountTarget; // For absolute regulation, position on which regulation is done SWORD SpeedFracError; // Fractionnal speed error of last speed update SBYTE MotorMaxSpeed; // For absolute regulation, maximum motor speed SBYTE MotorMaxAcceleration; // For absolute regulation, maximum motor acceleration }MOTORDATA; typedef struct { SLONG SyncTachoDif; SLONG SyncTurnParameter; SWORD SyncOldError; SWORD SyncAccError; }SYNCMOTORDATA; static MOTORDATA MotorData[3]; static SYNCMOTORDATA SyncData; static UBYTE RegulationTime; static UBYTE RegulationOptions; void dOutputInit(void) { UBYTE Temp; OUTPUTInit; ENABLECaptureMotorA; ENABLECaptureMotorB; ENABLECaptureMotorC; for (Temp = 0; Temp < 3; Temp++) { MOTORDATA * pMD = &(MotorData[Temp]); pMD->MotorSetSpeed = 0; pMD->MotorTargetSpeed = 0; pMD->MotorActualSpeed = 0; pMD->MotorRampUpCount = 0; pMD->MotorRampDownCount = 0; pMD->MotorRunState = 0; pMD->MotorTachoCountToRun = 0; pMD->MotorRunForever = 1; pMD->AccError = 0; pMD->PositionFracError = 0; pMD->RegulationTimeCount = 0; pMD->RegPParameter = DEFAULT_P_GAIN_FACTOR; pMD->RegIParameter = DEFAULT_I_GAIN_FACTOR; pMD->RegDParameter = DEFAULT_D_GAIN_FACTOR; pMD->MotorMaxSpeed = DEFAULT_MAX_SPEED; pMD->MotorMaxAcceleration = DEFAULT_MAX_ACCELERATION; pMD->RegulationMode = 0; pMD->MotorOverloaded = 0; INSERTMode(Temp, COAST_MOTOR_MODE); INSERTSpeed(Temp, pMD->MotorSetSpeed); } } /* This function is called every 1 mS and will go through all the motors and there dependencies */ /* Actual motor speed is only passed (updated) to the AVR controller form this function */ /* DeltacaptureCount used to count number of Tachocount within last 100 mS. Used with position control regulation */ /* CurrentCaptureCount used to tell total current position. Used to tell when movement has been obtained */ /* MotorBlockTachoCount tell current position within current movement. Reset when a new block is started from the VM */ /* RotationCaptureCount is additional counter for the rotationsensor. Uses it own value so it does conflict with other CaptureCount */ void dOutputCtrl(void) { UBYTE MotorNr; SLONG NewTachoCount[3]; TACHOCaptureReadResetAll(NewTachoCount[MOTOR_A], NewTachoCount[MOTOR_B], NewTachoCount[MOTOR_C]); for (MotorNr = 0; MotorNr < 3; MotorNr++) { MOTORDATA * pMD = &(MotorData[MotorNr]); pMD->DeltaCaptureCount += NewTachoCount[MotorNr]; pMD->CurrentCaptureCount += NewTachoCount[MotorNr]; pMD->MotorBlockTachoCount += NewTachoCount[MotorNr]; pMD->RotationCaptureCount += NewTachoCount[MotorNr]; pMD->RegulationTimeCount++; if (pMD->MotorRunState == MOTOR_RUN_STATE_RAMPUP) { dOutputRampUpFunction(MotorNr); } if (pMD->MotorRunState == MOTOR_RUN_STATE_RAMPDOWN) { dOutputRampDownFunction(MotorNr); } if (pMD->MotorRunState == MOTOR_RUN_STATE_RUNNING) { dOutputTachoLimitControl(MotorNr); } if (pMD->MotorRunState == MOTOR_RUN_STATE_IDLE) { dOutputMotorIdleControl(MotorNr); } if (pMD->MotorRunState == MOTOR_RUN_STATE_HOLD) { pMD->MotorSetSpeed = 0; pMD->MotorActualSpeed = 0; pMD->MotorTargetSpeed = 0; pMD->PositionFracError = 0; pMD->RegulationTimeCount = 0; pMD->DeltaCaptureCount = 0; pMD->MotorRunState = MOTOR_RUN_STATE_RUNNING; } if (pMD->RegulationTimeCount > RegulationTime) { pMD->RegulationTimeCount = 0; dOutputRegulateMotor(MotorNr); pMD->DeltaCaptureCount = 0; } } INSERTSpeed(MOTOR_A, MotorData[MOTOR_A].MotorActualSpeed); INSERTSpeed(MOTOR_B, MotorData[MOTOR_B].MotorActualSpeed); INSERTSpeed(MOTOR_C, MotorData[MOTOR_C].MotorActualSpeed); } void dOutputExit(void) { OUTPUTExit; } /* Called eveyr 1 mS */ /* Data mapping for controller (IO-Map is updated with these values) */ void dOutputGetMotorParameters(UBYTE *CurrentMotorSpeed, SLONG *TachoCount, SLONG *BlockTachoCount, UBYTE *RunState, UBYTE *MotorOverloaded, SLONG *RotationCount) { UBYTE Tmp; for (Tmp = 0; Tmp < 3; Tmp++) { MOTORDATA * pMD = &(MotorData[Tmp]); CurrentMotorSpeed[Tmp] = pMD->MotorActualSpeed; TachoCount[Tmp] = pMD->CurrentCaptureCount; BlockTachoCount[Tmp] = pMD->MotorBlockTachoCount; RotationCount[Tmp] = pMD->RotationCaptureCount; RunState[Tmp] = pMD->MotorRunState; MotorOverloaded[Tmp] = pMD->MotorOverloaded; } } void dOutputSetMode(UBYTE MotorNr, UBYTE Mode) //Set motor mode (break, Float) { INSERTMode(MotorNr, Mode); } /* Update the regulation state for the motor */ /* Need to reset regulation parameter depending on current status of the motor */ /* AccError & OldPositionError used for position regulation and Sync Parameter are used for synchronization regulation */ void dOutputEnableRegulation(UBYTE MotorNr, UBYTE RegulationMode) { MOTORDATA * pMD = &(MotorData[MotorNr]); pMD->RegulationMode = RegulationMode; if ((pMD->RegulationMode & REGSTATE_REGULATED) && (pMD->MotorSetSpeed == 0) && (pMD->MotorRunState != MOTOR_RUN_STATE_RAMPDOWN)) { pMD->AccError = 0; pMD->OldPositionError = 0; pMD->PositionFracError = 0; } if (pMD->RegulationMode & REGSTATE_SYNCHRONE) { if (((pMD->MotorActualSpeed == 0) || (pMD->TurnParameter != 0) || (pMD->TurnParameter == 0)) && (pMD->MotorRunState != MOTOR_RUN_STATE_RAMPDOWN)) { SyncData.SyncTachoDif = 0; SyncData.SyncAccError = 0; SyncData.SyncOldError = 0; SyncData.SyncTurnParameter = 0; } } } /* Disable current regulation if enabled */ void dOutputDisableRegulation(UBYTE MotorNr) { MotorData[MotorNr].RegulationMode = REGSTATE_IDLE; } /* Calling this function with reset count which tell current position and which is used to tell if the wanted position is obtained */ /* Calling this function will reset current movement of the motor if it is running */ void dOutputResetTachoLimit(UBYTE MotorNr) { MOTORDATA * pMD = &(MotorData[MotorNr]); pMD->CurrentCaptureCount = 0; pMD->MotorTachoCountToRun = 0; pMD->MotorTachoCountTarget = 0; if (pMD->RegulationMode & REGSTATE_SYNCHRONE) { dOutputResetSyncMotors(MotorNr); } if (pMD->MotorRunForever == 1) { pMD->MotorRunForever = 0; // To ensure that we get the same functionality for all combination on motor durations } } /* MotorBlockTachoCount tells current position in current movement. */ /* Used within the synchronization to compare current motor position. Reset on every new movement from the VM */ void dOutputResetBlockTachoLimit(UBYTE MotorNr) { MotorData[MotorNr].MotorBlockTachoCount = 0; } /* Additional counter add to help the VM application keep track of number of rotation for the rotation sensor */ /* This values can be reset independtly from the other tacho count values used with regulation and position control */ void dOutputResetRotationCaptureCount(UBYTE MotorNr) { MotorData[MotorNr].RotationCaptureCount = 0; } /* Can be used to set new PID values */ void dOutputSetPIDParameters(UBYTE MotorNr, UBYTE NewRegPParameter, UBYTE NewRegIParameter, UBYTE NewRegDParameter) { MOTORDATA * pMD = &(MotorData[MotorNr]); pMD->RegPParameter = NewRegPParameter; pMD->RegIParameter = NewRegIParameter; pMD->RegDParameter = NewRegDParameter; } /* Set maximum speed and acceleration */ void dOutputSetMax(UBYTE MotorNr, SBYTE NewMaxSpeed, SBYTE NewMaxAcceleration) { MOTORDATA * pMD = &(MotorData[MotorNr]); pMD->MotorMaxSpeed = NewMaxSpeed; pMD->MotorMaxAcceleration = NewMaxAcceleration; } /* Set new regulation time */ void dOutputSetRegulationTime(UBYTE NewRegulationTime) { RegulationTime = NewRegulationTime; } /* Set new regulation options */ void dOutputSetRegulationOptions(UBYTE NewRegulationOptions) { RegulationOptions = NewRegulationOptions; } /* Called to set TachoCountToRun which is used for position control for the model */ /* Must be called before motor start */ /* TachoCountToRun is calculated as a signed value */ void dOutputSetTachoLimit(UBYTE MotorNr, ULONG BlockTachoCntToTravel) { MOTORDATA * pMD = &(MotorData[MotorNr]); if (pMD->RegulationMode & REGSTATE_POSITION) { pMD->MotorRunForever = 0; pMD->MotorTachoCountToRun = BlockTachoCntToTravel; } else if (BlockTachoCntToTravel == 0) { pMD->MotorRunForever = 1; } else { pMD->MotorRunForever = 0; if (pMD->MotorSetSpeed == 0) { if (pMD->MotorTargetSpeed > 0) { pMD->MotorTachoCountToRun += BlockTachoCntToTravel; } else { pMD->MotorTachoCountToRun -= BlockTachoCntToTravel; } } else { if (pMD->MotorSetSpeed > 0) { pMD->MotorTachoCountToRun += BlockTachoCntToTravel; } else { pMD->MotorTachoCountToRun -= BlockTachoCntToTravel; } } } } /* This function is used for setting up the motor mode and motor speed */ void dOutputSetSpeed (UBYTE MotorNr, UBYTE NewMotorRunState, SBYTE Speed, SBYTE NewTurnParameter) { MOTORDATA * pMD = &(MotorData[MotorNr]); if ((pMD->MotorSetSpeed != Speed) || (pMD->MotorRunState != NewMotorRunState) || (NewMotorRunState == MOTOR_RUN_STATE_IDLE) || (pMD->TurnParameter != NewTurnParameter)) { if (pMD->MotorTargetSpeed == 0) { pMD->AccError = 0; pMD->OldPositionError = 0; pMD->PositionFracError = 0; pMD->RegulationTimeCount = 0; pMD->DeltaCaptureCount = 0; TACHOCountReset(MotorNr); } switch (NewMotorRunState) { case MOTOR_RUN_STATE_IDLE: { //pMD->MotorSetSpeed = 0; //pMD->MotorTargetSpeed = 0; //pMD->TurnParameter = 0; pMD->RegulationMode = REGSTATE_IDLE; } break; case MOTOR_RUN_STATE_RAMPUP: { if (pMD->MotorSetSpeed == 0) { pMD->MotorSetSpeed = Speed; pMD->TurnParameter = NewTurnParameter; pMD->MotorRampUpIncrement = 0; pMD->MotorRampTachoCountStart = pMD->CurrentCaptureCount; pMD->MotorRampUpCount = 0; } else { if (Speed > 0) { if (pMD->MotorSetSpeed >= Speed) { NewMotorRunState = MOTOR_RUN_STATE_RUNNING; } else { pMD->MotorSetSpeed = Speed; pMD->TurnParameter = NewTurnParameter; pMD->MotorRampUpIncrement = 0; pMD->MotorRampTachoCountStart = pMD->CurrentCaptureCount; pMD->MotorRampUpCount = 0; } } else { if (pMD->MotorSetSpeed <= Speed) { NewMotorRunState = MOTOR_RUN_STATE_RUNNING; } else { pMD->MotorSetSpeed = Speed; pMD->TurnParameter = NewTurnParameter; pMD->MotorRampUpIncrement = 0; pMD->MotorRampTachoCountStart = pMD->CurrentCaptureCount; pMD->MotorRampUpCount = 0; } } } } break; case MOTOR_RUN_STATE_RUNNING: { pMD->MotorSetSpeed = Speed; pMD->MotorTargetSpeed = Speed; pMD->TurnParameter = NewTurnParameter; if (pMD->MotorSetSpeed == 0) { NewMotorRunState = MOTOR_RUN_STATE_HOLD; } } break; case MOTOR_RUN_STATE_RAMPDOWN: { if (pMD->MotorTargetSpeed >= 0) { if (pMD->MotorSetSpeed <= Speed) { NewMotorRunState = MOTOR_RUN_STATE_RUNNING; } else { pMD->MotorSetSpeed = Speed; pMD->TurnParameter = NewTurnParameter; pMD->MotorRampDownIncrement = 0; pMD->MotorRampTachoCountStart = pMD->CurrentCaptureCount; pMD->MotorRampDownCount = 0; } } else { if (pMD->MotorSetSpeed >= Speed) { NewMotorRunState = MOTOR_RUN_STATE_RUNNING; } else { pMD->MotorSetSpeed = Speed; pMD->TurnParameter = NewTurnParameter; pMD->MotorRampDownIncrement = 0; pMD->MotorRampTachoCountStart = pMD->CurrentCaptureCount; pMD->MotorRampDownCount = 0; } } } break; } pMD->MotorRunState = NewMotorRunState; pMD->MotorOverloaded = 0; } } /* Function used for controlling the Ramp-up periode */ /* Ramp-up is done with 1 increment in speed every X number of TachoCount, where X depend on duration of the periode and the wanted speed */ void dOutputRampUpFunction(UBYTE MotorNr) { MOTORDATA * pMD = &(MotorData[MotorNr]); if (pMD->MotorTargetSpeed == 0) { if (pMD->MotorSetSpeed > 0) { pMD->MotorTargetSpeed = MIN_MOVEMENT_POWER; } else { pMD->MotorTargetSpeed = -MIN_MOVEMENT_POWER; } } else { if (pMD->MotorRampUpIncrement == 0) { SWORD delta = (SWORD)((pMD->MotorTachoCountToRun - pMD->MotorRampTachoCountStart) / (pMD->MotorSetSpeed - pMD->MotorTargetSpeed)); if (pMD->MotorSetSpeed > 0) { pMD->MotorRampUpIncrement = delta; } else { pMD->MotorRampUpIncrement = -delta; } pMD->MotorRampTachoCountOld = pMD->CurrentCaptureCount; } if (pMD->MotorSetSpeed > 0) { if (pMD->CurrentCaptureCount > (pMD->MotorRampTachoCountOld + pMD->MotorRampUpIncrement)) { pMD->MotorTargetSpeed += 1; pMD->MotorRampTachoCountOld = pMD->CurrentCaptureCount; pMD->MotorRampUpCount = 0; } else { if (!(pMD->RegulationMode & REGSTATE_REGULATED)) { pMD->MotorRampUpCount++; if (pMD->MotorRampUpCount > 100) { pMD->MotorRampUpCount = 0; pMD->MotorTargetSpeed++; } } } } else { if (pMD->CurrentCaptureCount < (pMD->MotorRampTachoCountOld + pMD->MotorRampUpIncrement)) { pMD->MotorTargetSpeed -= 1; pMD->MotorRampTachoCountOld = pMD->CurrentCaptureCount; pMD->MotorRampUpCount = 0; } else { if (!(pMD->RegulationMode & REGSTATE_REGULATED)) { pMD->MotorRampUpCount++; if (pMD->MotorRampUpCount > 100) { pMD->MotorRampUpCount = 0; pMD->MotorTargetSpeed--; } } } } } if (pMD->MotorSetSpeed > 0) { if ((pMD->CurrentCaptureCount - pMD->MotorRampTachoCountStart) >= (pMD->MotorTachoCountToRun - pMD->MotorRampTachoCountStart)) { pMD->MotorTargetSpeed = pMD->MotorSetSpeed; pMD->MotorRunState = MOTOR_RUN_STATE_IDLE; } } else { if ((pMD->CurrentCaptureCount + pMD->MotorRampTachoCountStart) <= (pMD->MotorTachoCountToRun + pMD->MotorRampTachoCountStart)) { pMD->MotorTargetSpeed = pMD->MotorSetSpeed; pMD->MotorRunState = MOTOR_RUN_STATE_IDLE; } } if (pMD->MotorSetSpeed > 0) { if (pMD->MotorTargetSpeed > pMD->MotorSetSpeed) { pMD->MotorTargetSpeed = pMD->MotorSetSpeed; } } else { if (pMD->MotorTargetSpeed < pMD->MotorSetSpeed) { pMD->MotorTargetSpeed = pMD->MotorSetSpeed; } } if (pMD->RegulationMode == REGSTATE_IDLE) { pMD->MotorActualSpeed = pMD->MotorTargetSpeed; } } /* Function used for controlling the Ramp-down periode */ /* Ramp-down is done with 1 decrement in speed every X number of TachoCount, where X depend on duration of the periode and the wanted speed */ void dOutputRampDownFunction(UBYTE MotorNr) { MOTORDATA * pMD = &(MotorData[MotorNr]); if (pMD->MotorRampDownIncrement == 0) { if (pMD->MotorTargetSpeed > 0) { if ((pMD->MotorTargetSpeed > MIN_MOVEMENT_POWER) && (pMD->MotorSetSpeed == 0)) { pMD->MotorRampDownIncrement = ((pMD->MotorTachoCountToRun - pMD->CurrentCaptureCount) / ((pMD->MotorTargetSpeed - pMD->MotorSetSpeed) - MIN_MOVEMENT_POWER)); } else { pMD->MotorRampDownIncrement = ((pMD->MotorTachoCountToRun - pMD->CurrentCaptureCount) / (pMD->MotorTargetSpeed - pMD->MotorSetSpeed)); } } else { if ((pMD->MotorTargetSpeed < -MIN_MOVEMENT_POWER) && (pMD->MotorSetSpeed == 0)) { pMD->MotorRampDownIncrement = (-((pMD->MotorTachoCountToRun - pMD->CurrentCaptureCount) / ((pMD->MotorTargetSpeed - pMD->MotorSetSpeed) + MIN_MOVEMENT_POWER))); } else { pMD->MotorRampDownIncrement = (-((pMD->MotorTachoCountToRun - pMD->CurrentCaptureCount) / (pMD->MotorTargetSpeed - pMD->MotorSetSpeed))); } } pMD->MotorRampTachoCountOld = pMD->CurrentCaptureCount; } if (pMD->MotorTargetSpeed > 0) { if (pMD->CurrentCaptureCount > (pMD->MotorRampTachoCountOld + (SLONG)pMD->MotorRampDownIncrement)) { pMD->MotorTargetSpeed--; if (pMD->MotorTargetSpeed < MIN_MOVEMENT_POWER) { pMD->MotorTargetSpeed = MIN_MOVEMENT_POWER; } pMD->MotorRampTachoCountOld = pMD->CurrentCaptureCount; pMD->MotorRampDownCount = 0; dOutputRampDownSynch(MotorNr); } else { if (!(pMD->RegulationMode & REGSTATE_REGULATED)) { pMD->MotorRampDownCount++; if (pMD->MotorRampDownCount > (UWORD)(30 * pMD->MotorRampDownIncrement)) { pMD->MotorRampDownCount = (UWORD)(20 * pMD->MotorRampDownIncrement); pMD->MotorTargetSpeed++; } } } } else { if (pMD->CurrentCaptureCount < (pMD->MotorRampTachoCountOld + (SLONG)pMD->MotorRampDownIncrement)) { pMD->MotorTargetSpeed++; if (pMD->MotorTargetSpeed > -MIN_MOVEMENT_POWER) { pMD->MotorTargetSpeed = -MIN_MOVEMENT_POWER; } pMD->MotorRampTachoCountOld = pMD->CurrentCaptureCount; pMD->MotorRampDownCount = 0; dOutputRampDownSynch(MotorNr); } else { if (!(pMD->RegulationMode & REGSTATE_REGULATED)) { pMD->MotorRampDownCount++; if (pMD->MotorRampDownCount > (UWORD)(30 * (-pMD->MotorRampDownIncrement))) { pMD->MotorRampDownCount = (UWORD)(20 * (-pMD->MotorRampDownIncrement)); pMD->MotorTargetSpeed--; } } } } if ((pMD->RegulationMode & REGSTATE_SYNCHRONE) && (pMD->TurnParameter != 0)) { dOutputSyncTachoLimitControl(MotorNr); if (pMD->MotorRunState == MOTOR_RUN_STATE_IDLE) { dOutputMotorReachedTachoLimit(MotorNr); } } else { if (pMD->MotorTargetSpeed > 0) { if (pMD->CurrentCaptureCount >= pMD->MotorTachoCountToRun) { dOutputMotorReachedTachoLimit(MotorNr); } } else { if (pMD->CurrentCaptureCount <= pMD->MotorTachoCountToRun) { dOutputMotorReachedTachoLimit(MotorNr); } } } if (pMD->RegulationMode == REGSTATE_IDLE) { pMD->MotorActualSpeed = pMD->MotorTargetSpeed; } } /* Function used to tell whether the wanted position is obtained */ void dOutputTachoLimitControl(UBYTE MotorNr) { MOTORDATA * pMD = &(MotorData[MotorNr]); if (pMD->RegulationMode & REGSTATE_POSITION) { /* No limit when doing absolute position regulation. */ return; } if (pMD->MotorRunForever == 0) { if (pMD->RegulationMode & REGSTATE_SYNCHRONE) { dOutputSyncTachoLimitControl(MotorNr); } else { if (pMD->MotorSetSpeed > 0) { if ((pMD->CurrentCaptureCount >= pMD->MotorTachoCountToRun)) { pMD->MotorRunState = MOTOR_RUN_STATE_IDLE; pMD->RegulationMode = REGSTATE_IDLE; } } else { if (pMD->MotorSetSpeed < 0) { if (pMD->CurrentCaptureCount <= pMD->MotorTachoCountToRun) { pMD->MotorRunState = MOTOR_RUN_STATE_IDLE; pMD->RegulationMode = REGSTATE_IDLE; } } } } } else { if (pMD->CurrentCaptureCount > MAX_COUNT_TO_RUN) { pMD->CurrentCaptureCount = 0; } if (pMD->MotorTargetSpeed != 0) { pMD->MotorTachoCountToRun = pMD->CurrentCaptureCount; } } if (pMD->RegulationMode == REGSTATE_IDLE) { pMD->MotorActualSpeed = pMD->MotorTargetSpeed; } } /* Function used to decrease speed slowly when the motor is set to idle */ void dOutputMotorIdleControl(UBYTE MotorNr) { INSERTMode(MotorNr, COAST_MOTOR_MODE); MOTORDATA * pMD = &(MotorData[MotorNr]); if (pMD->MotorActualSpeed != 0) { if (pMD->MotorActualSpeed > 0) { pMD->MotorActualSpeed--; } else { pMD->MotorActualSpeed++; } } if (pMD->MotorTargetSpeed != 0) { if (pMD->MotorTargetSpeed > 0) { pMD->MotorTargetSpeed--; } else { pMD->MotorTargetSpeed++; } } if (pMD->MotorSetSpeed != 0) { if (pMD->MotorSetSpeed > 0) { pMD->MotorSetSpeed--; } else { pMD->MotorSetSpeed++; } } } /* Check if Value is between [-Limit:Limit], and change it if it is not the case. */ SLONG dOutputBound(SLONG Value, SLONG Limit) { if (Value > Limit) return Limit; else if (Value < -Limit) return -Limit; else return Value; } /* Function called to evaluate which regulation princip that need to run and which MotorNr to use (I.E.: Which motors are synched together)*/ void dOutputRegulateMotor(UBYTE MotorNr) { UBYTE SyncMotorOne; UBYTE SyncMotorTwo; MOTORDATA * pMD = &(MotorData[MotorNr]); if (pMD->RegulationMode & REGSTATE_POSITION) { dOutputAbsolutePositionRegulation(MotorNr); } else if (pMD->RegulationMode & REGSTATE_REGULATED) { dOutputCalculateMotorPosition(MotorNr); } else { if (pMD->RegulationMode & REGSTATE_SYNCHRONE) { dOutputMotorSyncStatus(MotorNr, &SyncMotorOne, &SyncMotorTwo); if ((SyncMotorOne != 0xFF) &&(SyncMotorTwo != 0xFF)) { dOutputSyncMotorPosition(SyncMotorOne, SyncMotorTwo); } } } } /* Compute PID regulation result for a given error. */ SLONG dOutputPIDRegulation(UBYTE MotorNr, SLONG PositionError) { SLONG PValue, DValue, IValue, TotalRegValue; MOTORDATA *pMD = &MotorData[MotorNr]; PositionError = dOutputBound (PositionError, 32000); PValue = PositionError * (pMD->RegPParameter/REG_CONST_DIV); DValue = (PositionError - pMD->OldPositionError) * (pMD->RegDParameter/REG_CONST_DIV); pMD->OldPositionError = PositionError; pMD->AccError = (pMD->AccError * 3 + PositionError) / 4; pMD->AccError = dOutputBound (pMD->AccError, 800); IValue = pMD->AccError * (pMD->RegIParameter/REG_CONST_DIV); if (!(RegulationOptions & REGOPTION_NO_SATURATION)) { PValue = dOutputBound (PValue, REG_MAX_VALUE); IValue = dOutputBound (IValue, REG_MAX_VALUE); } TotalRegValue = (PValue + IValue + DValue) / 2; if (TotalRegValue > MAXIMUM_SPEED_FW) { TotalRegValue = MAXIMUM_SPEED_FW; pMD->MotorOverloaded = 1; } else if (TotalRegValue < MAXIMUM_SPEED_RW) { TotalRegValue = MAXIMUM_SPEED_RW; pMD->MotorOverloaded = 1; } return TotalRegValue; } /* Compute integer change for this regulation step, according to value and * previous fractional error. * Used for values which are expressed as "per SPEED_TIME" to translate them * in "per RegulationTime".*/ SLONG dOutputFractionalChange(SLONG Value, SWORD *FracError) { SLONG IntegerChange; /* Apply fractional change in case RegulationTime is different from * SPEED_TIME. In this case, fractional part is accumulated until it reach * one half (with "one" being SPEED_TIME). This is use the same principle * as the Bresenham algorithm. */ IntegerChange = Value * RegulationTime / SPEED_TIME; *FracError += Value * RegulationTime % SPEED_TIME; if (*FracError > SPEED_TIME / 2) { *FracError -= SPEED_TIME; IntegerChange++; } else if (*FracError < -SPEED_TIME / 2) { *FracError += SPEED_TIME; IntegerChange--; } return IntegerChange; } /* Filter speed according to motor maximum speed and acceleration. */ void dOutputSpeedFilter(UBYTE MotorNr, SLONG PositionDiff) { /* Inputs: * - PositionDiff: difference between current position and position to reach. * - MotorMaxAcceleration: maximum speed change per regulation period (or 0 for unlimited). * - MotorMaxSpeed: maximum motor speed (can not be zero, or do not call this function). * Output: * - MotorTargetSpeed: speed to regulate on motor. */ MOTORDATA *pMD = &MotorData[MotorNr]; SLONG IdealSpeed; SLONG PositionDiffAbs = ABS (PositionDiff); /* Should be able to brake on time. */ if (pMD->MotorMaxAcceleration && PositionDiffAbs < MAXIMUM_SPEED_FW * MAXIMUM_SPEED_FW / 2) { IdealSpeed = sqrtf (2 * PositionDiffAbs * pMD->MotorMaxAcceleration); IdealSpeed = dOutputBound (IdealSpeed, pMD->MotorMaxSpeed); } else { /* Do not go past consign. */ IdealSpeed = MIN (PositionDiffAbs, pMD->MotorMaxSpeed); } /* Apply sign. */ if (PositionDiff < 0) { IdealSpeed = -IdealSpeed; } /* Check max acceleration. */ SLONG SpeedDiff = IdealSpeed - pMD->MotorTargetSpeed; if (pMD->MotorMaxAcceleration) { SLONG MaxSpeedChange = dOutputFractionalChange (pMD->MotorMaxAcceleration, &pMD->SpeedFracError); SpeedDiff = dOutputBound (SpeedDiff, MaxSpeedChange); } pMD->MotorTargetSpeed += SpeedDiff; } /* Absolute position regulation. */ void dOutputAbsolutePositionRegulation(UBYTE MotorNr) { /* Inputs: * - CurrentCaptureCount: current motor position. * - MotorTachoCountToRun: wanted position, filtered with speed and acceleration. * * Outputs: * - MotorActualSpeed: power to be applied to motor. * - MotorOverloaded: set if MotorActualSpeed reached maximum. */ SLONG PositionChange; SLONG PositionError; SLONG TotalRegValue; MOTORDATA *pMD = &MotorData[MotorNr]; /* Position update. */ if (pMD->MotorMaxSpeed) { dOutputSpeedFilter (MotorNr, pMD->MotorTachoCountToRun - pMD->MotorTachoCountTarget); PositionChange = dOutputFractionalChange (pMD->MotorTargetSpeed * MAX_CAPTURE_COUNT / INPUT_SCALE_FACTOR, &pMD->PositionFracError); pMD->MotorTachoCountTarget += PositionChange; } else { pMD->MotorTachoCountTarget = pMD->MotorTachoCountToRun; } /* Regulation. */ PositionError = pMD->MotorTachoCountTarget - pMD->CurrentCaptureCount; TotalRegValue = dOutputPIDRegulation (MotorNr, PositionError); pMD->MotorActualSpeed = TotalRegValue; } /* Regulation function used when Position regulation is enabled */ /* The regulation form only control one motor at a time */ void dOutputCalculateMotorPosition(UBYTE MotorNr) { SLONG PositionError; SLONG TotalRegValue; SLONG PositionChange; MOTORDATA * pMD = &(MotorData[MotorNr]); PositionChange = dOutputFractionalChange (pMD->MotorTargetSpeed * MAX_CAPTURE_COUNT / INPUT_SCALE_FACTOR, &pMD->PositionFracError); PositionError = (pMD->OldPositionError - pMD->DeltaCaptureCount) + PositionChange; TotalRegValue = dOutputPIDRegulation (MotorNr, PositionError); pMD->MotorActualSpeed = (SBYTE)TotalRegValue; } /* Regulation function used when syncrhonization regulation is enabled */ /* The regulation form controls two motors at a time */ void dOutputSyncMotorPosition(UBYTE MotorOne, UBYTE MotorTwo) { SLONG TempTurnParameter; SLONG PValue; SLONG IValue; SLONG DValue; SLONG CorrectionValue; SLONG MotorSpeed; MOTORDATA * pOne = &(MotorData[MotorOne]); MOTORDATA * pTwo = &(MotorData[MotorTwo]); SyncData.SyncTachoDif = (SLONG)((pOne->MotorBlockTachoCount) - (pTwo->MotorBlockTachoCount)); if (pOne->TurnParameter != 0) { if ((pOne->MotorBlockTachoCount != 0) || (pTwo->MotorBlockTachoCount != 0)) { if (pOne->MotorTargetSpeed >= 0) { if (pOne->TurnParameter > 0) { TempTurnParameter = (SLONG)(((SLONG)pTwo->TurnParameter * (SLONG)pTwo->MotorTargetSpeed)/100); } else { TempTurnParameter = (SLONG)(((SLONG)pOne->TurnParameter * (SLONG)pOne->MotorTargetSpeed)/100); } } else { if (pOne->TurnParameter > 0) { TempTurnParameter = (SLONG)(((SLONG)pOne->TurnParameter * (-(SLONG)pOne->MotorTargetSpeed))/100); } else { TempTurnParameter = (SLONG)(((SLONG)pTwo->TurnParameter * (-(SLONG)pTwo->MotorTargetSpeed))/100); } } } else { TempTurnParameter = pOne->TurnParameter; } } else { TempTurnParameter = 0; } SyncData.SyncTurnParameter += (SLONG)(((TempTurnParameter * (MAX_CAPTURE_COUNT))/INPUT_SCALE_FACTOR)*2); //SyncTurnParameter should ophold difference between the two motors. SyncData.SyncTachoDif += SyncData.SyncTurnParameter; SyncData.SyncTachoDif = dOutputBound (SyncData.SyncTachoDif, 500); PValue = SyncData.SyncTachoDif * (pOne->RegPParameter/REG_CONST_DIV); DValue = (SyncData.SyncTachoDif - SyncData.SyncOldError) * (pOne->RegDParameter/REG_CONST_DIV); SyncData.SyncOldError = (SWORD)SyncData.SyncTachoDif; SyncData.SyncAccError += (SWORD)SyncData.SyncTachoDif; SyncData.SyncAccError = dOutputBound (SyncData.SyncAccError, 900); IValue = SyncData.SyncAccError * (pOne->RegIParameter/REG_CONST_DIV); CorrectionValue = (PValue + IValue + DValue) / 4; MotorSpeed = pOne->MotorTargetSpeed - CorrectionValue; MotorSpeed = dOutputBound (MotorSpeed, MAXIMUM_SPEED_FW); if (pOne->TurnParameter != 0) { if (pOne->MotorTargetSpeed > 0) { MotorSpeed = dOutputBound (MotorSpeed, pOne->MotorTargetSpeed); } else { MotorSpeed = dOutputBound (MotorSpeed, -pOne->MotorTargetSpeed); } } pOne->MotorActualSpeed = (SBYTE)MotorSpeed; MotorSpeed = pTwo->MotorTargetSpeed + CorrectionValue; MotorSpeed = dOutputBound (MotorSpeed, MAXIMUM_SPEED_FW); if (pOne->TurnParameter != 0) { if (pTwo->MotorTargetSpeed > 0) { MotorSpeed = dOutputBound (MotorSpeed, pTwo->MotorTargetSpeed); } else { MotorSpeed = dOutputBound (MotorSpeed, -pTwo->MotorTargetSpeed); } } pTwo->MotorActualSpeed = (SBYTE)MotorSpeed; } //Called when the motor is ramping down void dOutputMotorReachedTachoLimit(UBYTE MotorNr) { MOTORDATA * pOne = &(MotorData[MotorNr]); if (pOne->RegulationMode & REGSTATE_SYNCHRONE) { UBYTE MotorOne, MotorTwo; MotorOne = MotorNr; MotorTwo = 0xFF; UBYTE i; for(i = MOTOR_A; i <= MOTOR_C; i++) { if (i == MotorOne) continue; if (MotorData[i].RegulationMode & REGSTATE_SYNCHRONE) { MotorTwo = i; break; } } pOne->MotorSetSpeed = 0; pOne->MotorTargetSpeed = 0; pOne->MotorActualSpeed = 0; pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pOne->RegulationMode = REGSTATE_IDLE; if (MotorTwo != 0xFF) { MOTORDATA * pTwo = &(MotorData[MotorTwo]); pTwo->MotorSetSpeed = 0; pTwo->MotorTargetSpeed = 0; pTwo->MotorActualSpeed = 0; pTwo->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->RegulationMode = REGSTATE_IDLE; } } else { if (pOne->MotorSetSpeed == 0) { pOne->MotorTargetSpeed = 0; pOne->MotorActualSpeed = 0; } pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pOne->RegulationMode = REGSTATE_IDLE; } } /* Function used for control tacho limit when motors are synchronised */ /* Special control is needed when the motor are turning */ void dOutputSyncTachoLimitControl(UBYTE MotorNr) { UBYTE MotorOne, MotorTwo; MotorOne = MotorNr; MotorTwo = 0xFF; // Synchronisation is done two times, as this function is called for each // motor. This is the same behaviour as previous code. UBYTE i; for(i = MOTOR_A; i <= MOTOR_C; i++) { if (i == MotorOne) continue; if (MotorData[i].RegulationMode & REGSTATE_SYNCHRONE) { MotorTwo = i; break; } } if (MotorTwo == 0xFF) MotorOne = 0xFF; if ((MotorOne != 0xFF) && (MotorTwo != 0xFF)) { MOTORDATA * pOne = &(MotorData[MotorOne]); MOTORDATA * pTwo = &(MotorData[MotorTwo]); if (pOne->TurnParameter != 0) { if (pOne->TurnParameter > 0) { if (pTwo->MotorTargetSpeed >= 0) { if ((SLONG)(pTwo->CurrentCaptureCount >= pTwo->MotorTachoCountToRun)) { pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->MotorRunState = MOTOR_RUN_STATE_IDLE; pOne->CurrentCaptureCount = pTwo->CurrentCaptureCount; pOne->MotorTachoCountToRun = pTwo->MotorTachoCountToRun; } } else { if ((SLONG)(pOne->CurrentCaptureCount <= pOne->MotorTachoCountToRun)) { pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->CurrentCaptureCount = pOne->CurrentCaptureCount; pTwo->MotorTachoCountToRun = pOne->MotorTachoCountToRun; } } } else { if (pOne->MotorTargetSpeed >= 0) { if ((SLONG)(pOne->CurrentCaptureCount >= pOne->MotorTachoCountToRun)) { pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->CurrentCaptureCount = pOne->CurrentCaptureCount; pTwo->MotorTachoCountToRun = pOne->MotorTachoCountToRun; } } else { if ((SLONG)(pTwo->CurrentCaptureCount <= pTwo->MotorTachoCountToRun)) { pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->MotorRunState = MOTOR_RUN_STATE_IDLE; pOne->CurrentCaptureCount = pTwo->CurrentCaptureCount; pOne->MotorTachoCountToRun = pTwo->MotorTachoCountToRun; } } } } else { if (pOne->MotorSetSpeed > 0) { if ((pOne->CurrentCaptureCount >= pOne->MotorTachoCountToRun) || (pTwo->CurrentCaptureCount >= pTwo->MotorTachoCountToRun)) { pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->MotorRunState = MOTOR_RUN_STATE_IDLE; } } else { if (pOne->MotorSetSpeed < 0) { if ((pOne->CurrentCaptureCount <= pOne->MotorTachoCountToRun) || (pTwo->CurrentCaptureCount <= pTwo->MotorTachoCountToRun)) { pOne->MotorRunState = MOTOR_RUN_STATE_IDLE; pTwo->MotorRunState = MOTOR_RUN_STATE_IDLE; } } } } } } /* Function which can evaluate which motor are synched */ void dOutputMotorSyncStatus(UBYTE MotorNr, UBYTE *SyncMotorOne, UBYTE *SyncMotorTwo) { if (MotorNr < MOTOR_C) { if (MotorNr == MOTOR_A) { *SyncMotorOne = MotorNr; *SyncMotorTwo = *SyncMotorOne + 1; if (MotorData[*SyncMotorTwo].RegulationMode & REGSTATE_SYNCHRONE) { //Synchronise motor A & B } else { *SyncMotorTwo = *SyncMotorOne + 2; if (MotorData[*SyncMotorTwo].RegulationMode & REGSTATE_SYNCHRONE) { //Synchronise motor A & C } else { //Only Motor A has Sync setting => Do nothing, treat motor as motor without regulation *SyncMotorTwo = 0xFF; } } } if (MotorNr == MOTOR_B) { *SyncMotorOne = MotorNr; *SyncMotorTwo = *SyncMotorOne + 1; if (MotorData[*SyncMotorTwo].RegulationMode & REGSTATE_SYNCHRONE) { if (!(MotorData[MOTOR_A].RegulationMode & REGSTATE_SYNCHRONE)) { //Synchronise motor B & C } } else { //Only Motor B has Sync settings or Motor is sync. with Motor A and has therefore already been called *SyncMotorTwo = 0xFF; } } } else { *SyncMotorOne = 0xFF; *SyncMotorTwo = 0xFF; } } /* Function which is called when motors are synchronized and the motor position is reset */ void dOutputResetSyncMotors(UBYTE MotorNr) { UBYTE MotorOne, MotorTwo; MotorOne = MotorNr; MotorTwo = 0xFF; UBYTE i; for(i = MOTOR_A; i <= MOTOR_C; i++) { if (i == MotorOne) continue; if (MotorData[i].RegulationMode & REGSTATE_SYNCHRONE) { MotorTwo = i; break; } } if (MotorTwo == 0xFF) MotorOne = 0xFF; MOTORDATA * pMD = &(MotorData[MotorNr]); if ((MotorOne != 0xFF) && (MotorTwo != 0xFF)) { MOTORDATA * pTwo = &(MotorData[MotorTwo]); pMD->CurrentCaptureCount = 0; pMD->MotorTachoCountToRun = 0; pMD->MotorTachoCountTarget = 0; pTwo->CurrentCaptureCount = 0; pTwo->MotorTachoCountToRun = 0; pTwo->MotorTachoCountTarget = 0; } else { pMD->CurrentCaptureCount = 0; pMD->MotorTachoCountToRun = 0; pMD->MotorTachoCountTarget = 0; } } /* Function which is called when motors are synchronized and motor is ramping down */ void dOutputRampDownSynch(UBYTE MotorNr) { UBYTE MotorOne, MotorTwo; MotorOne = MotorNr; MotorTwo = 0xFF; UBYTE i; for(i = MOTOR_A; i <= MOTOR_C; i++) { if (i == MotorOne) continue; if (MotorData[i].RegulationMode & REGSTATE_SYNCHRONE) { MotorTwo = i; break; } } if (MotorTwo == 0xFF) MotorOne = 0xFF; if ((MotorOne != 0xFF) && (MotorTwo != 0xFF)) { MOTORDATA * pOne = &(MotorData[MotorOne]); MOTORDATA * pTwo = &(MotorData[MotorTwo]); if (pOne->TurnParameter != 0) { if (pOne->TurnParameter > 0) { if (pOne->MotorTargetSpeed >= 0) { if (pTwo->MotorActualSpeed < 0) { pTwo->MotorTargetSpeed--; } } else { if (pTwo->MotorActualSpeed > 0) { pTwo->MotorTargetSpeed++; } } } else { if (pOne->MotorTargetSpeed >= 0) { if (pTwo->MotorActualSpeed < 0) { pTwo->MotorTargetSpeed--; } } else { if (pTwo->MotorActualSpeed > 0) { pTwo->MotorTargetSpeed++; } } } } } } nxt-firmware-1.29.7/src/d_output.h000066400000000000000000000076051466344546000170640ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_output.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_outp $ // // Platform C // #ifndef D_OUTPUT #define D_OUTPUT #define NEW_MOTOR #ifdef NEW_MOTOR //Constant reffering to new motor #define REG_CONST_DIV 32 // Constant which the PID constants value will be divided with #define DEFAULT_P_GAIN_FACTOR 96//3 #define DEFAULT_I_GAIN_FACTOR 32//1 #define DEFAULT_D_GAIN_FACTOR 32//1 #define MIN_MOVEMENT_POWER 10 #define MAX_CAPTURE_COUNT 100 #else //Constant reffering to Old motor #define REG_CONST_DIV 1 // Constant which the PID constants value will be divided with #define DEFAULT_P_GAIN_FACTOR 3 #define DEFAULT_I_GAIN_FACTOR 1 #define DEFAULT_D_GAIN_FACTOR 1 #define MIN_MOVEMENT_POWER 30 #define MAX_CAPTURE_COUNT 80 #endif #define DEFAULT_MAX_SPEED 80 #define DEFAULT_MAX_ACCELERATION 20 #define REGULATION_TIME 100 // Measured in 1 mS, regulation interval //Constant reffering to RegMode parameter #define REGSTATE_IDLE 0x00 #define REGSTATE_REGULATED 0x01 #define REGSTATE_SYNCHRONE 0x02 #define REGSTATE_POSITION 0x04 //Constant reffering to RunState parameter #define MOTOR_RUN_STATE_IDLE 0x00 #define MOTOR_RUN_STATE_RAMPUP 0x10 #define MOTOR_RUN_STATE_RUNNING 0x20 #define MOTOR_RUN_STATE_RAMPDOWN 0x40 #define MOTOR_RUN_STATE_HOLD 0x60 // Constants related to Regulation Options #define REGOPTION_NO_SATURATION 0x01 // Do not limit intermediary regulation results enum { MOTOR_A, MOTOR_B, MOTOR_C }; void dOutputInit(void); void dOutputExit(void); void dOutputCtrl(void); void dOutputGetMotorParameters(UBYTE *CurrentMotorSpeed, SLONG *TachoCount, SLONG *BlockTachoCount, UBYTE *RunState, UBYTE *MotorOverloaded, SLONG *RotationCount); void dOutputSetMode(UBYTE MotorNr, UBYTE Mode); void dOutputSetSpeed (UBYTE MotorNr, UBYTE NewMotorRunState, SBYTE Speed, SBYTE TurnParameter); void dOutputEnableRegulation(UBYTE MotorNr, UBYTE RegulationMode); void dOutputDisableRegulation(UBYTE MotorNr); void dOutputSetTachoLimit(UBYTE MotorNr, ULONG TachoCntToTravel); void dOutputResetTachoLimit(UBYTE MotorNr); void dOutputResetBlockTachoLimit(UBYTE MotorNr); void dOutputResetRotationCaptureCount(UBYTE MotorNr); void dOutputSetPIDParameters(UBYTE MotorNr, UBYTE NewRegPParameter, UBYTE NewRegIParameter, UBYTE NewRegDParameter); void dOutputSetMax(UBYTE MotorNr, SBYTE NewMaxSpeed, SBYTE NewMaxAcceleration); void dOutputSetRegulationTime(UBYTE NewRegulationTime); void dOutputSetRegulationOptions(UBYTE NewRegulationOptions); void dOutputRegulateMotor(UBYTE MotorNr); void dOutputCalculateRampUpParameter(UBYTE MotorNr, ULONG NewTachoLimit); void dOutputRampDownFunction(UBYTE MotorNr); void dOutputRampUpFunction(UBYTE MotorNr); void dOutputTachoLimitControl(UBYTE MotorNr); void dOutputAbsolutePositionRegulation(UBYTE MotorNr); void dOutputCalculateMotorPosition(UBYTE MotorNr); void dOutputSyncMotorPosition(UBYTE MotorOne, UBYTE MotorTwo); void dOutputMotorReachedTachoLimit(UBYTE MotorNr); void dOutputMotorIdleControl(UBYTE MotorNr); void dOutputSyncTachoLimitControl(UBYTE MotorNr); void dOutputMotorSyncStatus(UBYTE MotorNr, UBYTE *SyncMotorOne, UBYTE *SyncMotorTwo); void dOutputResetSyncMotors(UBYTE MotorNr); #endif nxt-firmware-1.29.7/src/d_output.r000066400000000000000000000400251466344546000170670ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_output.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_outp $ // // Platform C // #ifdef SAM7S256 #if defined (PROTOTYPE_PCB_3) || (PROTOTYPE_PCB_4) #define MOTOR_A_DIR AT91C_PIO_PA1 #define MOTOR_A_INT AT91C_PIO_PA15 #define MOTOR_B_DIR AT91C_PIO_PA9 #define MOTOR_B_INT AT91C_PIO_PA26 #define MOTOR_C_DIR AT91C_PIO_PA8 #define MOTOR_C_INT AT91C_PIO_PA0 #else #define MOTOR_A_DIR AT91C_PIO_PA1 #define MOTOR_A_INT AT91C_PIO_PA15 #define MOTOR_B_DIR AT91C_PIO_PA9 #define MOTOR_B_INT AT91C_PIO_PA26 #define MOTOR_C_DIR AT91C_PIO_PA8 #define MOTOR_C_INT AT91C_PIO_PA27 #endif #define FORWARD 0x01 #define REVERSE -0x01 #define TIMER_0_ID12 (1L << AT91C_ID_TC0) #define TIMER_1_ID13 (1L << AT91C_ID_TC1) #define TIMER_2_ID14 (1L << AT91C_ID_TC2) typedef struct { SLONG TachoCountTable; SLONG TachoCountTableOld; SBYTE MotorDirection; }TACHOPARAMETERS; static TACHOPARAMETERS MotorTachoValue[3]; #define OUTPUTInit {\ UBYTE Tmp;\ for (Tmp = 0; Tmp < NOS_OF_AVR_OUTPUTS; Tmp++)\ {\ IoToAvr.PwmValue[Tmp] = 0;\ }\ IoToAvr.OutputMode = 0x00;\ IoToAvr.PwmFreq = 8;\ } #define INSERTSpeed(Motor, Speed) IoToAvr.PwmValue[Motor] = Speed #define INSERTMode(Motor, Mode) if (Mode & 0x02)\ {\ IoToAvr.OutputMode |= (0x01 << Motor);\ }\ else\ {\ IoToAvr.OutputMode &= ~(0x01 << Motor);\ } #define ENABLEDebugOutput {\ *AT91C_PIOA_PER = 0x20000000; /* Enable PIO on PA029 */\ *AT91C_PIOA_OER = 0x20000000; /* PA029 set to Output */\ } #define SETDebugOutputHigh *AT91C_PIOA_SODR = 0x20000000 #define SETDebugOutputLow *AT91C_PIOA_CODR = 0x20000000 #define ENABLECaptureMotorA {\ *AT91C_PIOA_PDR = MOTOR_A_INT; /* Disable PIO on PA15 */\ *AT91C_PIOA_BSR = MOTOR_A_INT; /* Enable Peripheral B on PA15 */\ *AT91C_PIOA_PPUDR = MOTOR_A_INT | MOTOR_A_DIR; /* Disable Pull Up resistor on PA15 & PA1 */\ *AT91C_PIOA_PER = MOTOR_A_DIR; /* Enable PIO on PA1 */\ *AT91C_PIOA_ODR = MOTOR_A_DIR; /* PA1 set to input */\ *AT91C_PIOA_IFER = MOTOR_A_INT | MOTOR_A_DIR; /* Enable filter on PA15 & PA1 */\ *AT91C_PMC_PCER = TIMER_1_ID13; /* Enable clock for TC1*/\ *AT91C_TCB_BMR = AT91C_TCB_TC1XC1S_NONE; /* No external clock signal XC2 */\ *AT91C_TCB_BCR = 0x0; /* Clear SYNC */\ *AT91C_TC1_CMR = *AT91C_TC1_CMR & 0X00000000; /* Clear all bits in TC1_CMR */\ *AT91C_TC1_CMR = *AT91C_TC1_CMR & 0xFFFF7FFF; /* Enable capture mode */\ *AT91C_TC1_CMR = *AT91C_TC1_CMR | AT91C_TC_CLKS_TIMER_DIV5_CLOCK; /* Set clock for timer to Clock5 = div 1024*/\ *AT91C_TC1_CMR = *AT91C_TC1_CMR | AT91C_TC_ABETRG; /* Use external trigger for TIO1*/\ *AT91C_TC1_CMR = *AT91C_TC1_CMR | AT91C_TC_EEVTEDG_BOTH; /* Trigger on both edges */\ *AT91C_TC1_CMR = *AT91C_TC1_CMR | AT91C_TC_LDRA_RISING; /* RA loading register set */\ *AT91C_AIC_IDCR = TIMER_1_ID13; /* Irq controller setup */\ AT91C_AIC_SVR[13] = (unsigned int)CaptureAInt; \ AT91C_AIC_SMR[13] = 0x05; /* Enable trigger on level */\ *AT91C_AIC_ICCR = TIMER_1_ID13; /* Clear interrupt register PID13*/\ *AT91C_TC1_IDR = 0xFF; /* Disable all interrupt from TC1 */\ *AT91C_TC1_IER = 0x80; /* Enable interrupt from external trigger */\ *AT91C_AIC_IECR = TIMER_1_ID13; /* Enable interrupt from TC1 */\ *AT91C_TC1_CCR = 0x00; /* Clear registers before setting */\ *AT91C_TC1_CCR = AT91C_TC_CLKEN; /* Enable clock */\ } #define ENABLECaptureMotorB {\ *AT91C_PIOA_PDR = MOTOR_B_INT; /* Disable PIO on PA26 */\ *AT91C_PIOA_BSR = MOTOR_B_INT; /* Enable Peripheral B on PA26 */\ *AT91C_PIOA_PER = MOTOR_B_DIR; /* Enable PIO on PA09 */\ *AT91C_PIOA_PPUDR = MOTOR_B_INT | MOTOR_B_DIR; /* Disable Pull Up resistor on PA26 & PA09 */\ *AT91C_PIOA_ODR = MOTOR_B_DIR; /* PA09 set to input */\ *AT91C_PIOA_IFER = MOTOR_B_INT | MOTOR_B_DIR; /* Enable filter on PA26 & PA09 */\ *AT91C_PMC_PCER = TIMER_2_ID14; /* Enable clock for TC2*/\ *AT91C_TCB_BMR = AT91C_TCB_TC2XC2S_NONE; /* No external clock signal */\ *AT91C_TCB_BCR = 0x0; /* Clear SYNC */\ *AT91C_TC2_CMR = *AT91C_TC2_CMR & 0X00000000; /* Clear all bits in TC1_CMR */\ *AT91C_TC2_CMR = *AT91C_TC2_CMR & 0xFFFF7FFF; /* Enable capture mode */\ *AT91C_TC2_CMR = *AT91C_TC2_CMR | AT91C_TC_CLKS_TIMER_DIV5_CLOCK; /* Set clock for timer to Clock5 = div 1024*/\ *AT91C_TC2_CMR = *AT91C_TC2_CMR | AT91C_TC_ABETRG; /* Use external trigger for TIO2*/\ *AT91C_TC2_CMR = *AT91C_TC2_CMR | AT91C_TC_EEVTEDG_BOTH; /* Trigger on both edges */\ *AT91C_TC2_CMR = *AT91C_TC2_CMR | AT91C_TC_LDRA_RISING; /* RA loading register set */\ *AT91C_AIC_IDCR = TIMER_2_ID14; /* Irq controller setup */\ AT91C_AIC_SVR[14] = (unsigned int)CaptureBInt; \ AT91C_AIC_SMR[14] = 0x05; /* Enable trigger on level */\ *AT91C_AIC_ICCR = TIMER_2_ID14; /* Clear interrupt register PID14*/\ *AT91C_TC2_IDR = 0xFF; /* Disable all interrupt from TC2 */\ *AT91C_TC2_IER = 0x80; /* Enable interrupts from external trigger */\ *AT91C_AIC_IECR = TIMER_2_ID14; /* Enable interrupt from TC2 */\ *AT91C_TC2_CCR = 0x00; /* Clear registers before setting */\ *AT91C_TC2_CCR = AT91C_TC_CLKEN; /* Enable clock */\ } #define ENABLECaptureMotorC {\ *AT91C_PIOA_PDR = MOTOR_C_INT; /* Disable PIO on PA0 */\ *AT91C_PIOA_BSR = MOTOR_C_INT; /* Enable Peripheral B on PA0 */\ *AT91C_PIOA_PER = MOTOR_C_DIR; /* Enable PIO on PA08 */\ *AT91C_PIOA_PPUDR = MOTOR_C_INT | MOTOR_C_DIR; /* Disable Pull Up resistor on PA0 & PA08 */\ *AT91C_PIOA_ODR = MOTOR_C_DIR; /* PA08 set to input */\ *AT91C_PIOA_IFER = MOTOR_C_INT | MOTOR_C_DIR; /* Enable filter on PA26 & PA09 */\ *AT91C_PMC_PCER = TIMER_0_ID12; /* Enable clock for TC0*/\ *AT91C_TCB_BMR = AT91C_TCB_TC0XC0S_NONE; /* No external clock signal */\ *AT91C_TC0_CMR = *AT91C_TC0_CMR & 0X00000000; /* Clear all bits in TC0_CMR */\ *AT91C_TC0_CMR = *AT91C_TC0_CMR & 0xFFFF7FFF; /* Enable capture mode */\ *AT91C_TC0_CMR = *AT91C_TC0_CMR | AT91C_TC_CLKS_TIMER_DIV5_CLOCK; /* Set clock for timer to Clock5 = div 1024*/\ *AT91C_TC0_CMR = *AT91C_TC0_CMR | AT91C_TC_ABETRG; /* Use external trigger for TI0*/\ *AT91C_TC0_CMR = *AT91C_TC0_CMR | AT91C_TC_EEVTEDG_BOTH; /* Trigger on both edges */\ *AT91C_TC0_CMR = *AT91C_TC0_CMR | AT91C_TC_LDRA_RISING; /* RA loading register set */\ *AT91C_AIC_IDCR = TIMER_0_ID12; /* Disable interrupt */\ AT91C_AIC_SVR[12] = (unsigned int)CaptureCInt; \ AT91C_AIC_SMR[12] = 0x05; /* Enable trigger on level */\ *AT91C_AIC_ICCR = TIMER_0_ID12; /* Clear interrupt register PID12*/\ *AT91C_TC0_IDR = 0xFF; /* Disable all interrupt from TC0 */\ *AT91C_TC0_IER = 0x80; /* Enable interrupts from external trigger */\ *AT91C_AIC_IECR = TIMER_0_ID12; /* Enable interrupt from TC0 */\ *AT91C_TC0_CCR = 0x00; /* Clear registers before setting */\ *AT91C_TC0_CCR = AT91C_TC_CLKEN; /* Enable clock */\ } __ramfunc void CaptureAInt(void) { if (*AT91C_TC1_SR & AT91C_TC_MTIOA) { if (*AT91C_PIOA_PDSR & MOTOR_A_DIR) { MotorTachoValue[0].MotorDirection = REVERSE; //Motor is running reverse MotorTachoValue[0].TachoCountTable--; } else { MotorTachoValue[0].MotorDirection = FORWARD; //Motor is running forward MotorTachoValue[0].TachoCountTable++; } } else { if (*AT91C_PIOA_PDSR & MOTOR_A_DIR) { MotorTachoValue[0].MotorDirection = FORWARD; MotorTachoValue[0].TachoCountTable++; } else { MotorTachoValue[0].MotorDirection = REVERSE; MotorTachoValue[0].TachoCountTable--; } } } __ramfunc void CaptureBInt(void) { if (*AT91C_TC2_SR & AT91C_TC_MTIOA) { if (*AT91C_PIOA_PDSR & MOTOR_B_DIR) { MotorTachoValue[1].MotorDirection = REVERSE; //Motor is running reverse MotorTachoValue[1].TachoCountTable--; } else { MotorTachoValue[1].MotorDirection = FORWARD; //Motor is running forward MotorTachoValue[1].TachoCountTable++; } } else { if (*AT91C_PIOA_PDSR & MOTOR_B_DIR) { MotorTachoValue[1].MotorDirection = FORWARD; MotorTachoValue[1].TachoCountTable++; } else { MotorTachoValue[1].MotorDirection = REVERSE; MotorTachoValue[1].TachoCountTable--; } } } //__ramfunc void CaptureBInt(void) //{ // if (((bool)(*AT91C_TC2_SR & AT91C_TC_MTIOA))==((bool)(*AT91C_PIOA_PDSR & MOTOR_B_DIR))) // { // MotorTachoValue[1].MotorDirection = REVERSE; //Motor is running reverse // MotorTachoValue[1].TachoCountTable--; // } // else // { // MotorTachoValue[1].MotorDirection = FORWARD; //Motor is running reverse // MotorTachoValue[1].TachoCountTable++; // } //} __ramfunc void CaptureCInt(void) { if (*AT91C_TC0_SR & AT91C_TC_MTIOA) { if (*AT91C_PIOA_PDSR & MOTOR_C_DIR) { MotorTachoValue[2].MotorDirection = REVERSE; //Motor is running reverse MotorTachoValue[2].TachoCountTable--; } else { MotorTachoValue[2].MotorDirection = FORWARD; //Motor is running forward MotorTachoValue[2].TachoCountTable++; } } else { if (*AT91C_PIOA_PDSR & MOTOR_C_DIR) { MotorTachoValue[2].MotorDirection = FORWARD; MotorTachoValue[2].TachoCountTable++; } else { MotorTachoValue[2].MotorDirection = REVERSE; MotorTachoValue[2].TachoCountTable--; } } } #define OUTPUTExit {\ *AT91C_AIC_IDCR = TIMER_0_ID12 | TIMER_1_ID13 | TIMER_2_ID14; /* Disable interrupts for the timers */\ *AT91C_AIC_ICCR = TIMER_0_ID12 | TIMER_1_ID13 | TIMER_2_ID14; /* Clear penting interrupt register for timers*/\ *AT91C_PMC_PCDR = TIMER_0_ID12 | TIMER_1_ID13 | TIMER_2_ID14; /* Disable the clock for each of the timers*/\ *AT91C_PIOA_PER = MOTOR_A_DIR | MOTOR_A_INT | MOTOR_B_DIR | MOTOR_B_INT | MOTOR_C_DIR | MOTOR_C_INT; /* Enable PIO on PA15, PA11, PA26, PA09, PA27 & PA08 */\ *AT91C_PIOA_ODR = MOTOR_A_DIR | MOTOR_A_INT | MOTOR_B_DIR | MOTOR_B_INT | MOTOR_C_DIR | MOTOR_C_INT; /* Set to input PA15, PA11, PA26, PA09, PA27 & PA08 */\ *AT91C_PIOA_PPUDR = MOTOR_A_DIR | MOTOR_A_INT | MOTOR_B_DIR | MOTOR_B_INT | MOTOR_C_DIR | MOTOR_C_INT; /* Enable Pullup on PA15, PA11, PA26, PA09, PA27 & PA08 */\ } #define TACHOCountReset(MotorNr) {\ MotorTachoValue[MotorNr].TachoCountTable = 0;\ MotorTachoValue[MotorNr].TachoCountTableOld = 0;\ } #define TACHOCaptureReadResetAll(MotorDataA,MotorDataB,MotorDataC){\ MotorDataA = (MotorTachoValue[MOTOR_A].TachoCountTable - MotorTachoValue[MOTOR_A].TachoCountTableOld);\ MotorTachoValue[MOTOR_A].TachoCountTableOld = MotorTachoValue[MOTOR_A].TachoCountTable;\ MotorDataB = (MotorTachoValue[MOTOR_B].TachoCountTable - MotorTachoValue[MOTOR_B].TachoCountTableOld);\ MotorTachoValue[MOTOR_B].TachoCountTableOld = MotorTachoValue[MOTOR_B].TachoCountTable;\ MotorDataC = (MotorTachoValue[MOTOR_C].TachoCountTable - MotorTachoValue[MOTOR_C].TachoCountTableOld);\ MotorTachoValue[MOTOR_C].TachoCountTableOld = MotorTachoValue[MOTOR_C].TachoCountTable;\ } #define GetMotorDirection(MotorNr) MotorTachoValue[MotorNr].MotorDirection #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/d_sound.c000066400000000000000000000023541466344546000166430ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_sound.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_soun $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_sound.h" #include "d_sound.r" void dSoundInit(void) { SOUNDInit; } void dSoundVolume(UBYTE Step) { SOUNDVolume(Step); } UBYTE dSoundReady(void) { return (SOUNDReady); } UBYTE dSoundStart(UBYTE *pSound,UWORD Length,UWORD SampleRate, UBYTE FileType) { return (SOUNDStart(pSound,Length,SampleRate,FileType)); } UBYTE dSoundStop(void) { return (SOUNDStop); } UBYTE dSoundTone(UBYTE *pMelody,UWORD Length,UBYTE Volume) { return (SOUNDTone(pMelody,Length,Volume)); } void dSoundFreq(UWORD Hz,UWORD mS,UBYTE Volume) { SOUNDFreq(Hz,mS,Volume); } void dSoundExit(void) { SOUNDExit; } nxt-firmware-1.29.7/src/d_sound.h000066400000000000000000000025411466344546000166460ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_sound.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_soun $ // // Platform C // #ifndef D_SOUND #define D_SOUND void dSoundInit(void); void dSoundVolume(UBYTE Step); UBYTE dSoundReady(void); UBYTE dSoundStart(UBYTE *pSound,UWORD Length,UWORD SampleRate, UBYTE FileFormat); UBYTE dSoundStop(void); UBYTE dSoundTone(UBYTE *pMelody,UWORD Length,UBYTE Volume); void dSoundFreq(UWORD Hz,UWORD mS,UBYTE Volume); void dSoundExit(void); #define SOUNDVOLUMESTEPS 4 #define DURATION_MIN 10 // [mS] #define FREQUENCY_MIN 220 // [Hz] #define FREQUENCY_MAX 14080 // [Hz] #define SAMPLERATE_MIN 2000 // Min sample rate [sps] #define SAMPLERATE_DEFAULT 8000 // Default sample rate [sps] #define SAMPLERATE_MAX 16000 // Max sample rate [sps] #endif nxt-firmware-1.29.7/src/d_sound.r000066400000000000000000000366371466344546000166750ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_sound.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_soun $ // // Platform C // #include "d_sound_adpcm.r" #ifdef SAM7S256 #define SAMPLEMIN 0 // Must be zero (no pwm/interrupt) #define SAMPLEMAX 256 // Must be 256 (8 bit wave format) #define SAMPLECENTER (((SAMPLEMAX - SAMPLEMIN) / 2) + SAMPLEMIN) #define SAMPLEWORD ULONG #define SAMPLEWORDS 8 #define SAMPLEWORDBITS (sizeof(SAMPLEWORD) * 8) #define SAMPLEBITS (SAMPLEWORDS * SAMPLEWORDBITS) #define SAMPLECONSTANT 3 // >> == (SAMPLEMAX / SAMPLEWORDBITS) #define SAMPLETONENO 16 // No of tone samples #define SAMPLEBUFFERS 2 #define INIT_PREV_VAL_ADPCM 0x7F #define INIT_INDEX_ADPCM 20 SAMPLEWORD SampleBuffer[SAMPLEBUFFERS][SAMPLEWORDS]; SAMPLEWORD ToneBuffer[SAMPLETONENO]; const SAMPLEWORD TonePattern[SOUNDVOLUMESTEPS + 1][SAMPLETONENO] = { { 0xF0F0F0F0,0xF0F0F0F0, // Step 0 = silence 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0 }, { 0xF0F0F0F0,0xF0F0F0F0, // Step 1 = 1/512 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F8, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0 }, { 0xF0F0F0F0,0xF0F0F0F0, // Step 2 = 0,+3,+4,+3,0,-3,-4,-3 0xF0F0F0F0,0xF0F8F8F8, 0xF0F0F8F8,0xF8F8F0F0, 0xF8F8F8F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0F0F0F0, 0xF0F0F0F0,0xF0E0E0E0, 0xF0F0E0E0,0xE0E0F0F0, 0xE0E0E0F0,0xF0F0F0F0 }, { 0xF0F0F0F0,0xF0F0F0F0, // Step 3 = 0,+10,+14,+10,0,-10,-14,-10 0xF8F8F8F8,0xF8F8FCFC, 0xF8F8FCFC,0xFCFCFCFC, 0xFCFCF8F8,0xF8F8F8F8, 0xF0F0F0F0,0xF0F0F0F0, 0xE0E0E0E0,0xE0E0C0C0, 0xE0E0C0C0,0xC0C0C0C0, 0xC0C0E0E0,0xE0E0E0E0 }, { 0xF0F0F0F0,0xF0F0F0F0, // Step 4 = 0,+22,+32,+22,0,-22,-32,-22 0xFCFCFCFC,0xFCFCFDFD, 0xFFFFFFFF,0xFFFFFFFF, 0xFDFDFCFC,0xFCFCFCFC, 0xF0F0F0F0,0xF0F0F0F0, 0xC0C0C0C0,0xC0C08080, 0x00000000,0x00000000, 0x8080C0C0,0xC0C0C0C0 } }; __ramdata UBYTE FractionPattern[SAMPLEWORDS] = { 0x00, // 0 -> 00000000 0x10, // 1 -> 00010000 0x22, // 2 -> 00100010 0x4A, // 3 -> 01001010 0x55, // 4 -> 01010101 0x6D, // 5 -> 01101101 0x77, // 6 -> 01110111 0x7F, // 7 -> 01111111 }; typedef struct { SWORD Valprev; // Previous output value SWORD Index; // Index into stepsize table }ADPCM_State; ULONG ToneCycles; // No of tone cycles ULONG ToneCyclesReady; // No of tone cycles for ready ULONG ClockNext; // Serial clock for next buffer UBYTE *pSoundPointer; // Pointer to sample in actual sound buffer UBYTE *pSoundPointerNext; // Pointer to sample in next sound buffer UWORD SoundSamplesLeft; // Number of samples left on actual sound buffer UWORD SoundSamplesLeftNext; // Number of samples left on next sound buffer UBYTE SampleBufferNo; // Sample buffer no in use UBYTE SoundReady; // Sound channel ready (idle) UBYTE SoundDivider; // Volume UWORD MelodyPointer; UBYTE CurrentFileFormat; // Hold current playing file type UBYTE Outdata[2]; // Output buffer used within the ADPCM algorithm ADPCM_State State; // Struct holding ADPCM state #define SOUNDIntEnable {\ *AT91C_SSC_IER = AT91C_SSC_ENDTX;\ } #define SOUNDIntDisable {\ *AT91C_SSC_IDR = AT91C_SSC_ENDTX;\ } #define SOUNDEnable {\ *AT91C_PIOA_PDR = AT91C_PA17_TD; /* Enable TD on PA17 */\ } #define SOUNDDisable {\ *AT91C_PIOA_PER = AT91C_PA17_TD; /* Disable TD on PA17 */\ } ULONG SoundSampleRate(UWORD Rate) { ULONG Result; if (Rate > SAMPLERATE_MAX) { Rate = SAMPLERATE_MAX; } if (Rate < SAMPLERATE_MIN) { Rate = SAMPLERATE_MIN; } Result = ((OSC / (2 * SAMPLEBITS)) / Rate) + 1; return (Result); } __ramfunc void CalculateBitstream(SAMPLEWORD *pSampleBuffer,UBYTE Sample) { ULONG IntegerMask; ULONG FractionMask; UBYTE Integer; UBYTE Fraction; UBYTE Mask; UBYTE Tmp; SWORD STmp; if (SoundDivider) { STmp = Sample; STmp &= 0xFF; STmp -= SAMPLECENTER; STmp >>= (SOUNDVOLUMESTEPS - SoundDivider); STmp += SAMPLECENTER; Sample = (UBYTE)STmp; SOUNDEnable; } else { SOUNDDisable; } Tmp = 0; IntegerMask = 0xFFFF0000; Integer = Sample >> SAMPLECONSTANT; Fraction = Sample - (Integer << SAMPLECONSTANT); IntegerMask = 0xFFFFFFFF << (SAMPLEWORDBITS - Integer); FractionMask = (IntegerMask >> 1) | IntegerMask; Mask = FractionPattern[Fraction]; while (Tmp < SAMPLEWORDS) { if ((Mask & (0x01 << Tmp))) { *pSampleBuffer = FractionMask; } else { *pSampleBuffer = IntegerMask; } pSampleBuffer++; Tmp++; } } __ramfunc void SscHandler(void) { static UBYTE ByteCnt = 0; if (SoundSamplesLeft) { if (0 == CurrentFileFormat) { CalculateBitstream(SampleBuffer[SampleBufferNo],*pSoundPointer); *AT91C_SSC_TNPR = (unsigned int)SampleBuffer[SampleBufferNo]; *AT91C_SSC_TNCR = SAMPLEWORDS; pSoundPointer++; SoundSamplesLeft--; if (!SoundSamplesLeft) { pSoundPointer = pSoundPointerNext; SoundSamplesLeft = SoundSamplesLeftNext; *AT91C_SSC_CMR = ClockNext; SoundSamplesLeftNext = 0; } if (++SampleBufferNo >= SAMPLEBUFFERS) { SampleBufferNo = 0; } } else { if (0 == ByteCnt) { SoundADPCMDecoder(*pSoundPointer, Outdata, &State.Valprev, &State.Index); CalculateBitstream(SampleBuffer[SampleBufferNo],Outdata[0]); *AT91C_SSC_TNPR = (unsigned int)SampleBuffer[SampleBufferNo]; *AT91C_SSC_TNCR = SAMPLEWORDS; if (++SampleBufferNo >= SAMPLEBUFFERS) { SampleBufferNo = 0; } ByteCnt++; } else { CalculateBitstream(SampleBuffer[SampleBufferNo],Outdata[1]); *AT91C_SSC_TNPR = (unsigned int)SampleBuffer[SampleBufferNo]; *AT91C_SSC_TNCR = SAMPLEWORDS; pSoundPointer++; SoundSamplesLeft--; if (!SoundSamplesLeft) { pSoundPointer = pSoundPointerNext; SoundSamplesLeft = SoundSamplesLeftNext; *AT91C_SSC_CMR = ClockNext; SoundSamplesLeftNext = 0; } if (++SampleBufferNo >= SAMPLEBUFFERS) { SampleBufferNo = 0; } ByteCnt = 0; } } } else { if (ToneCycles) { ToneCycles--; if (ToneCycles < ToneCyclesReady) { SoundReady = TRUE; } *AT91C_SSC_TNPR = (unsigned int)ToneBuffer; *AT91C_SSC_TNCR = SAMPLETONENO; if (SoundDivider) { SOUNDEnable; } else { SOUNDDisable; } } else { SoundReady = TRUE; SOUNDDisable; SOUNDIntDisable; } } } UBYTE SoundStart(UBYTE *Sound,UWORD Length,UWORD SampleRate, UBYTE NewFileFormat) { UBYTE Result = FALSE; if (SoundReady == TRUE) { if (Length > 1) { CurrentFileFormat = NewFileFormat; *AT91C_SSC_CMR = SoundSampleRate(SampleRate); pSoundPointer = Sound; SoundSamplesLeft = Length; if (0 == CurrentFileFormat) { CalculateBitstream(SampleBuffer[0],*pSoundPointer); *AT91C_SSC_TPR = (unsigned int)SampleBuffer[0]; *AT91C_SSC_TCR = SAMPLEWORDS; pSoundPointer++; SoundSamplesLeft--; CalculateBitstream(SampleBuffer[1],*pSoundPointer); *AT91C_SSC_TNPR = (unsigned int)SampleBuffer[1]; *AT91C_SSC_TNCR = SAMPLEWORDS; pSoundPointer++; SoundSamplesLeft--; } else { State.Valprev = INIT_PREV_VAL_ADPCM; State.Index = INIT_INDEX_ADPCM; SoundADPCMDecoder(*pSoundPointer, Outdata, &State.Valprev, &State.Index); CalculateBitstream(SampleBuffer[0],Outdata[0]); *AT91C_SSC_TPR = (unsigned int)SampleBuffer[0]; *AT91C_SSC_TCR = SAMPLEWORDS; pSoundPointer++; SoundSamplesLeft--; CalculateBitstream(SampleBuffer[1],Outdata[1]); *AT91C_SSC_TNPR = (unsigned int)SampleBuffer[1]; *AT91C_SSC_TNCR = SAMPLEWORDS; } SampleBufferNo = 0; SoundReady = FALSE; SOUNDIntEnable; *AT91C_SSC_PTCR = AT91C_PDC_TXTEN; } Result = TRUE; } else { if (!ToneCycles) { if (!SoundSamplesLeftNext) { CurrentFileFormat = NewFileFormat; ClockNext = SoundSampleRate(SampleRate); pSoundPointerNext = Sound; SoundSamplesLeftNext = Length; Result = TRUE; } } } return (Result); } UBYTE SoundStop(void) { ToneCycles = 0; SOUNDIntDisable; SOUNDDisable; SoundReady = TRUE; SoundSamplesLeft = 0; SoundSamplesLeftNext = 0; MelodyPointer = 0; return (TRUE); } void SoundVolume(UBYTE Step) { if (Step > SOUNDVOLUMESTEPS) { Step = SOUNDVOLUMESTEPS; } SoundDivider = Step; } void SoundFreq(UWORD Freq,UWORD mS,UBYTE Step) { UBYTE Tmp; if (mS < DURATION_MIN) { mS = DURATION_MIN; } if (Freq) { if (Freq < FREQUENCY_MIN) { Freq = FREQUENCY_MIN; } if (Freq > FREQUENCY_MAX) { Freq = FREQUENCY_MAX; } if (Step > SOUNDVOLUMESTEPS) { Step = SOUNDVOLUMESTEPS; } } else { Step = 0; Freq = 1000; } SoundDivider = Step; SoundSamplesLeft = 0; SoundSamplesLeftNext = 0; for (Tmp = 0;Tmp < SAMPLETONENO;Tmp++) { ToneBuffer[Tmp] = TonePattern[Step][Tmp]; } *AT91C_SSC_CMR = (((ULONG)OSC / (2L * 512L)) / ((ULONG)Freq)) + 1L; ToneCycles = ((ULONG)Freq * (ULONG)mS) / 1000L - 1L; ToneCyclesReady = ((ULONG)Freq * (ULONG)2L) / 1000L + 1L; *AT91C_SSC_TNPR = (unsigned int)ToneBuffer; *AT91C_SSC_TNCR = SAMPLETONENO; *AT91C_SSC_PTCR = AT91C_PDC_TXTEN; SoundReady = FALSE; SOUNDIntEnable; } UBYTE SoundTone(UBYTE *pMel,UWORD Length,UBYTE Step) { UBYTE Result = FALSE; UWORD Freq; UWORD mS; if ((SoundReady == TRUE)) { if (MelodyPointer <= (Length - 4)) { Freq = (UWORD)pMel[MelodyPointer++] << 8; Freq += (UWORD)pMel[MelodyPointer++]; mS = (UWORD)pMel[MelodyPointer++] << 8; mS += (UWORD)pMel[MelodyPointer++]; SoundFreq(Freq,mS,Step); } else { MelodyPointer = 0; Result = TRUE; } } return (Result); } #define SOUNDInit {\ SOUNDIntDisable;\ SoundReady = TRUE;\ MelodyPointer = 0;\ *AT91C_PMC_PCER = (1L << AT91C_ID_SSC); /* Enable MCK clock */\ *AT91C_PIOA_PER = AT91C_PA17_TD; /* Disable TD on PA17 */\ *AT91C_PIOA_ODR = AT91C_PA17_TD;\ *AT91C_PIOA_OWDR = AT91C_PA17_TD;\ *AT91C_PIOA_MDDR = AT91C_PA17_TD;\ *AT91C_PIOA_PPUDR = AT91C_PA17_TD;\ *AT91C_PIOA_IFDR = AT91C_PA17_TD;\ *AT91C_PIOA_CODR = AT91C_PA17_TD;\ *AT91C_PIOA_IDR = AT91C_PA17_TD;\ *AT91C_SSC_CR = AT91C_SSC_SWRST;\ AT91C_AIC_SVR[AT91C_ID_SSC] = (unsigned int)SscHandler;\ AT91C_AIC_SMR[AT91C_ID_SSC] = AT91C_AIC_PRIOR_LOWEST | AT91C_AIC_SRCTYPE_INT_EDGE_TRIGGERED; /* Set priority */\ *AT91C_SSC_TCMR = AT91C_SSC_CKS_DIV + AT91C_SSC_CKO_CONTINOUS + AT91C_SSC_START_CONTINOUS;\ *AT91C_SSC_TFMR = (SAMPLEWORDBITS - 1) + ((SAMPLEWORDS & 0xF) << 8) + AT91C_SSC_MSBF;\ *AT91C_SSC_CR = AT91C_SSC_TXEN; /* TX enable */\ *AT91C_AIC_ICCR = (1L << AT91C_ID_SSC); /* Clear interrupt */\ *AT91C_AIC_IECR = (1L << AT91C_ID_SSC); /* Enable int. controller */\ } #define SOUNDVolume(V) SoundVolume((UBYTE)V) #define SOUNDReady SoundReady #define SOUNDStart(pSnd,Lng,SR,FT) SoundStart(pSnd,Lng,SR,FT) #define SOUNDStop SoundStop() #define SOUNDTone(pMel,Lng,Vol) SoundTone(pMel,Lng,Vol) #define SOUNDFreq(Freq,Duration,Vol) SoundFreq(Freq,Duration,Vol) #define SOUNDExit {\ SOUNDIntDisable;\ SOUNDDisable;\ *AT91C_AIC_IDCR = (1L << AT91C_ID_SSC);\ } #endif nxt-firmware-1.29.7/src/d_sound_adpcm.r000066400000000000000000000123721466344546000200270ustar00rootroot00000000000000//Playback of compressed sound files. This additional feature is being brought to you under the following license. //Please adhere to its terms. //The original code includes minor changes to function correctly within the LEGO MINDSTORMS NXT embedded system, //but the main architecture are implemented as within the original code. //*********************************************************** //Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The //Netherlands. // // All Rights Reserved // //Permission to use, copy, modify, and distribute this software and its //documentation for any purpose and without fee is hereby granted, //provided that the above copyright notice appear in all copies and that //both that copyright notice and this permission notice appear in //supporting documentation, and that the names of Stichting Mathematisch //Centrum or CWI not be used in advertising or publicity pertaining to //distribution of the software without specific, written prior permission. // //STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO //THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE //FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES //WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN //ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT //OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // //******************************************************************/ // // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_sound_adpcm.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_sound_adpc $ // // Platform C // #ifdef SAM7S256 __ramdata static SWORD IndexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8, }; __ramdata static SWORD StepsizeTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; __ramfunc void SoundADPCMDecoder(UBYTE Indata, UBYTE *Outdata, SWORD *pStateValprev, SWORD *pStateIndex) { SWORD Step; // Stepsize SWORD Valprev; // Virtual previous output value SWORD Vpdiff; // Current change to valprev SWORD Index; // Current step change index UBYTE *pOut; // Output buffer pointer UBYTE Sign; // Current adpcm sign bit UBYTE Delta; // Current adpcm output value UBYTE Bufferstep; // Toggle between High og Low nibble UBYTE Len; // Nibble Counter pOut = Outdata; Valprev = *pStateValprev; Index = *pStateIndex; Step = StepsizeTable[Index]; Bufferstep = 0; Len = 2; for (; Len > 0 ; Len--) //Step 1 - get the delta value and compute next index { if(Bufferstep) { Delta = Indata & 0x0F; } else { Delta = (Indata >> 4) & 0x0F; } Bufferstep = !Bufferstep; Index += IndexTable[Delta]; //Step 2 - Find new index value (for later) if (Index < 0) { Index = 0; } else { if (Index > 88) { Index = 88; } } Sign = Delta & 8; //Step 3 - Separate sign and magnitude Delta = Delta & 7; Vpdiff = Step >> 3; //Step 4 - Compute difference and new predicted value if (Delta & 4) { Vpdiff += Step; } if (Delta & 2) { Vpdiff += Step>>1; } if (Delta & 1) { Vpdiff += Step>>2; } if (Sign) Valprev -= Vpdiff; else Valprev += Vpdiff; if (Valprev > 255) //Step 5 - clamp output value { Valprev = 255; } else { if (Valprev < 0) { Valprev = 0; } } Step = StepsizeTable[Index]; //Step 6 - Update step value *pOut++ = (UBYTE)Valprev; //Step 7 - Output value } *pStateValprev = Valprev; //State.Valprev = Valprev; *pStateIndex = Index; //State.Index = Index; } #endif nxt-firmware-1.29.7/src/d_timer.c000066400000000000000000000021171466344546000166300ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date: 23-04-08 11:15 $ // // Filename $Workfile:: d_timer.c $ // // Version $Revision: 2 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_time $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_timer.h" #include "d_timer.r" void dTimerInit(void) { TIMERInit; } ULONG dTimerRead(void) { ULONG V; TIMERReadAlt(V) return (V); } ULONG dTimerReadNoPoll(void) { return (Timer1mS); } ULONG dTimerReadHiRes(void) { // return ((*AT91C_PITC_PIIR)/3); following code is equivalent and about five times faster, see Hacker's Delight or exact division ULONG tmp= ((*AT91C_PITC_PIIR)*2863311531); if(tmp > 2863311531) return tmp - 2863311531; else if(tmp > 1431655766) return tmp - 1431655766; else return tmp; } ULONG dTimerGetNextMSTickCnt(void) { return NextTimerValue; } void dTimerExit(void) { TIMERExit; } nxt-firmware-1.29.7/src/d_timer.h000066400000000000000000000011651466344546000166370ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date: 23-04-08 11:15 $ // // Filename $Workfile:: d_timer.h $ // // Version $Revision: 2 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_time $ // // Platform C // #ifndef D_TIMER #define D_TIMER void dTimerInit(void); ULONG dTimerRead(void); ULONG dTimerReadNoPoll(void); ULONG dTimerReadHiRes(void); ULONG dTimerGetNextMSTickCnt(void); #define dTimerReadTicks() (*AT91C_PITC_PIIR) void dTimerExit(void); #endif nxt-firmware-1.29.7/src/d_timer.r000066400000000000000000000051011466344546000166430ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 23-04-08 11:15 $ // // Filename $Workfile:: d_timer.r $ // // Version $Revision:: 2 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_time $ // // Platform C // #ifdef SAM7S256 #define MS_1_TIME ((OSC/16)/1000) static ULONG TimerValue; static ULONG NextTimerValue; static ULONG Timer1mS; /* PIT timer is used as main timer - timer interval is 1mS */ #define TIMERInit TimerValue = ((*AT91C_PITC_PIIR) & AT91C_PITC_CPIV);\ NextTimerValue = (((*AT91C_PITC_PIIR) + MS_1_TIME) & AT91C_PITC_CPIV);\ Timer1mS = 0 #define TIMERRead(V) if (MS_1_TIME < ((((*AT91C_PITC_PIIR) & AT91C_PITC_CPIV) - TimerValue) & AT91C_PITC_CPIV))\ {\ TimerValue += MS_1_TIME;\ TimerValue &= AT91C_PITC_CPIV;\ Timer1mS++;\ }\ V = Timer1mS #define TIMERReadAlt(V) if((SLONG)((*AT91C_PITC_PIIR) - NextTimerValue) >= 0)\ {\ Timer1mS ++;\ NextTimerValue += MS_1_TIME;\ }\ V = Timer1mS;\ #define TIMERReadSkip(V) diff= (((*AT91C_PITC_PIIR)) - NextTimerValue);\ if (diff >= 0)\ {\ diff /= MS_1_TIME;\ diff += 1;\ Timer1mS += diff;\ diff *= MS_1_TIME;\ NextTimerValue += diff;\ }\ V = Timer1mS;\ #define TIMERExit #endif //SAM7S256 #ifdef _WINDOWS #include #include #define TIMERInit timeBeginPeriod(1); #define TIMERRead(V) (V) = timeGetTime(); #define TIMERExit timeEndPeriod(1); #endif //_WINDOWS nxt-firmware-1.29.7/src/d_usb.c000066400000000000000000000736451466344546000163170ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_usb.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_usb. $ // // Platform C // #include "stdconst.h" #include "m_sched.h" #include "d_usb.h" #include "d_usb.r" #define ENDPOINT_OUT 1 // HOST write #define ENDPOINT_OUT_SIZE 64 #define ENDPOINT_IN 2 // HOST read #define ENDPOINT_IN_SIZE 64 #define AT91C_UDP_ISR ((AT91_REG *) 0xFFFB001C) // (UDP) Interrupt Status Register #define AT91C_RSTC_URSTEN ((unsigned int) 0x1 << 0) // (RSTC) User Reset Enable // Endpoint Control and Status Registers #define AT91C_UDP_CSR0 ((AT91_REG *) 0xFFFB0030) // Endpoint 0 Control and Status Register #define AT91C_UDP_CSR1 ((AT91_REG *) 0xFFFB0034) // Endpoint 1 Control and Status Register #define AT91C_UDP_CSR2 ((AT91_REG *) 0xFFFB0038) // Endpoint 2 Control and Status Register #define AT91C_UDP_CSR3 ((AT91_REG *) 0xFFFB003C) // Endpoint 3 Control and Status Register // Endpoint FIFO Data Registers #define AT91C_UDP_FDR0 ((AT91_REG *) 0xFFFB0050) // Endpoint 0 FIFO Data Register #define AT91C_UDP_FDR1 ((AT91_REG *) 0xFFFB0054) // Endpoint 1 FIFO Data Register #define AT91C_UDP_FDR2 ((AT91_REG *) 0xFFFB0058) // Endpoint 2 FIFO Data Register #define AT91C_UDP_FDR3 ((AT91_REG *) 0xFFFB005C) // Endpoint 3 FIFO Data Register const UBYTE DeviceDescriptor[] = { /* Device descriptor */ 0x12, // bLength, size of this descriptor = 18 entries 0x01, // bDescriptorType = 1 = DEVICE 0x00, // bcdUSBL, USB spec. vers. 2.0 0x02, // bcdUSBH, - 0x00, // bDeviceClass 0x00, // bDeviceSubclass 0x00, // bDeviceProtocol 0x08, // bMaxPacketSize0, EndPointZero packet size = 8 0x94, // idVendorL, LEGO Group 0x06, // idVendorH, - 0x02, // idProductL, LEGO USB IR Tower = 0x01 0x00, // idProductH, - 0x00, // bcdDeviceL, device is version (zero) 0x00, // bcdDeviceH, - 0x00, // iManufacturer, index of string descriptor describing manufacturer 0x00, // iProduct, index of string descriptor describing product 0x01, // iSerialNumber, index of string descriptor describing the device's // serial no. 0x01 // bNumConfigs, number of possible configurations (only one) }; /* USB standard request codes */ #define STD_GET_STATUS_ZERO 0x0080 #define STD_GET_STATUS_INTERFACE 0x0081 #define STD_GET_STATUS_ENDPOINT 0x0082 #define STD_CLEAR_FEATURE_ZERO 0x0100 #define STD_CLEAR_FEATURE_INTERFACE 0x0101 #define STD_CLEAR_FEATURE_ENDPOINT 0x0102 #define STD_SET_FEATURE_ZERO 0x0300 #define STD_SET_FEATURE_INTERFACE 0x0301 #define STD_SET_FEATURE_ENDPOINT 0x0302 #define STD_SET_ADDRESS 0x0500 #define STD_GET_DESCRIPTOR 0x0680 #define STD_SET_DESCRIPTOR 0x0700 #define STD_GET_CONFIGURATION 0x0880 #define STD_SET_CONFIGURATION 0x0900 #define STD_GET_INTERFACE 0x0A81 #define STD_SET_INTERFACE 0x0B01 #define STD_SYNCH_FRAME 0x0C82 /* USB constants, masks etc. */ #define END_OF_BUS_RESET ((unsigned int) 0x1 << 12) #define SUSPEND_INT ((unsigned int) 0x1 << 8) #define SUSPEND_RESUME ((unsigned int) 0x1 << 9) #define WAKEUP ((unsigned int) 0x1 << 13) //USB spec allows 500ms for control transfers #define USB_MAX_TIMEOUT 500 static UBYTE UsbHandleList[MAX_HANDLES]; static UBYTE UsbHandleCnt; static UWORD RequestedData; static UBYTE BrickNameKnown; enum { USB_NOT_CONFIGURED, USB_CONFIGURED, USB_CONFIGURED_BUT_SUSPENDED }; static UBYTE UsbConnectionStates; const UBYTE ConfigurationDescriptor[] = { /* ============== CONFIGURATION 1 =========== */ /* Configuration 1 descriptor */ 0x09, // bLength, descriptor size in bytes 0x02, // bDescriptorType, The constant Configuration 0x20, // wTotalLengthL for 2 EP + Control 0x00, // wTotalLengthH - 0x01, // bNumInterfaces, Number of interfaces in the configuration 0x01, // bConfigurationValue, Identifier for // Set_Configuration and Get_Configuration requests 0x00, // iConfiguration, Index of string descriptor for the configuration 0xC0, // bmAttributes, Bit 7 shall always be set. See e.g. page 108 in the book: // "USB Complete" by Jan Axelson. June 2001 // Self powered only bit 6 = 1 (zero = buspowered USB 1.1 and up) 0x00, // MaxPower, power required (mA./2) We're SELF-POWERED, so ZERO /* Interface Descriptor */ 0x09, // bLength, descriptor size in bytes 0x04, // bDescriptorType, the constant 0x04 = "INTERFACE" 0x00, // bInterfaceNumber, No. identifying this interface 0x00, // bAlternateSetting, value used to get an alternative interface 0x02, // bNumEndpoints, No. of supported endpoints in addition to endpoint 0 0xFF, // bInterfaceClass, Specifies the class code = VENDOR Specific 0xFF, // bInterfaceSubclass, Specifies the subclass code = VENDOR Specific 0xFF, // bInterfaceProtocol, protocol code = VENDOR Specific 0x00, // iInterface, index of string descriptor for the interface /* Endpoint 1 descriptor */ 0x07, // bLength, descriptor length incl. this = 7 0x05, // bDescriptorType 0x01, // bEndpointAddress, Endpoint 01 - OUT 0x02, // bmAttributes BULK ENDPOINT_OUT_SIZE, // wMaxPacketSize 0x00, // - 0x00, // bInterval /* Endpoint 2 descriptor */ 0x07, // bLength, descriptor length incl. this = 7 0x05, // bDescriptorType 0x82, // bEndpointAddress, Endpoint 02 - IN 0x02, // bmAttributes BULK ENDPOINT_IN_SIZE, // wMaxPacketSize 0x00, // - 0x00 // bInterval }; UBYTE SerialNumberDescriptor[] = { 0x1A, // bLength, descriptor length incl. this = 16 bytes 0x03, // bDescriptorType 0x31, 0x00, // MSD of Lap (Lap[2,3]) in UNICode 0x32, 0x00, // Lap[4,5] 0x33, 0x00, // Lap[6,7] 0x34, 0x00, // Lap[8,9] 0x35, 0x00, // Lap[10,11] 0x36, 0x00, // Lap[12,13] 0x37, 0x00, // Lap[14,15] 0x38, 0x00, // LSD of Lap (Lap[16,17]) in UNICode 0x30, 0x00, // MSD of Nap (Nap[18,19]) in UNICode 0x30, 0x00, // LSD of Nap (Nap[20,21]) in UNICode 0x39, 0x00, // MSD of Uap in UNICode 0x30, 0x00 // LSD of Uap in UNICode }; const UBYTE LangIdDescriptor[] = { 0x04, // Length 0x03, // Type, 3 = CONSTANT String 0x09, // English 0x04 // subcode = U.S. English }; static UCHAR CurrentConfiguration; // Configured or not. We've only 1 conf. so... Boolean static ULONG CurrentReceiveBank; // Used for keep track of the PING-PONG buffers ULONG g_UsbTimeoutCounter; #define MIN(a, b) (((a) < (b)) ? (a) : (b)) void dUsbDisconnect(void) { USBDisconnect; } void dUsbConnect(void) { USBConnect; } void dUsbStartTimeoutTimer(void) { g_UsbTimeoutCounter = 0; USBGetActualTime; } // A longer version of the USB timer. // Table 7-14 of the USB 2.0 spec allows up to 500ms for standard request completion. UBYTE dUsbTimedOut(void) { if(USBTimedOut) { g_UsbTimeoutCounter++; USBGetActualTime; } return (g_UsbTimeoutCounter >= USB_MAX_TIMEOUT) ? TRUE : FALSE; } UBYTE ConvertHighToHex(UBYTE TempChar) { TempChar = (TempChar >> 4) & 0x0F; if (TempChar > 0x09) TempChar += 0x37; else TempChar += 0x30; return TempChar; } UBYTE ConvertLowToHex(UBYTE TempChar) { TempChar &= 0x0F; if (TempChar > 0x09) TempChar += 0x37; else TempChar += 0x30; return TempChar; } void dUsbStoreBtAddress(UBYTE *pBtAddress) { UBYTE NoToConvert; // make the Lap human readable (hmmm Hexadecimal) NoToConvert = *pBtAddress++; SerialNumberDescriptor[2] = ConvertHighToHex(NoToConvert); SerialNumberDescriptor[4] = ConvertLowToHex(NoToConvert); NoToConvert = *pBtAddress++; SerialNumberDescriptor[6] = ConvertHighToHex(NoToConvert); SerialNumberDescriptor[8] = ConvertLowToHex(NoToConvert); NoToConvert = *pBtAddress++; SerialNumberDescriptor[10] = ConvertHighToHex(NoToConvert); SerialNumberDescriptor[12] = ConvertLowToHex(NoToConvert); NoToConvert = *pBtAddress++; SerialNumberDescriptor[14] = ConvertHighToHex(NoToConvert); SerialNumberDescriptor[16] = ConvertLowToHex(NoToConvert); // make the Uap human readable (hmmm Hexadecimal) NoToConvert = *pBtAddress++; SerialNumberDescriptor[18] = ConvertHighToHex(NoToConvert); SerialNumberDescriptor[20] = ConvertLowToHex(NoToConvert); // make the Nap human readable (hmmm Hexadecimal) NoToConvert = *pBtAddress++; SerialNumberDescriptor[22] = ConvertHighToHex(NoToConvert); SerialNumberDescriptor[24] = ConvertLowToHex(NoToConvert); USBConnect; // We're ready to participate in the real world BrickNameKnown = TRUE; // OK for referencing :-) } ULONG dUsbRead(UBYTE *pData, ULONG Length) { ULONG PacketSize, NumberOfBytesReceived; NumberOfBytesReceived = 0; while (Length) // Wished read size from user (Max length) { if ( !(BrickNameKnown)) // Right Brick??? break; if ( !(dUsbIsConfigured()) ) break; // Not configured - no time to waste if ( (*AT91C_UDP_CSR1) & CurrentReceiveBank ) // Data packet rx'ed in Current bank? { PacketSize = MIN((*AT91C_UDP_CSR1) >> 16, Length); // Normalize number of bytes available in FIFO Length -= PacketSize; // Rest of data to receive if (PacketSize < ENDPOINT_OUT_SIZE) // If data less, we only have one loop Length = 0; while(PacketSize--) // While more data in this very packet... pData[NumberOfBytesReceived++] = *AT91C_UDP_FDR1; // Fill in buffer *AT91C_UDP_CSR1 &= ~(CurrentReceiveBank); // Reset current bank pointer if (CurrentReceiveBank == AT91C_UDP_RX_DATA_BK0) // Current Receive Bank 0? CurrentReceiveBank = AT91C_UDP_RX_DATA_BK1; // We better use Bank 1 else CurrentReceiveBank = AT91C_UDP_RX_DATA_BK0; // Okay, go for Bank 0 :-) } else Length = 0; // Leave and let's use the CPU cycles in a better way } return NumberOfBytesReceived; // Size of actually received stuff } ULONG dUsbWrite( const UBYTE *pData, ULONG Length) { ULONG CharsEachTx = 0; // Send the very first (or only) packet CharsEachTx = MIN(Length, ENDPOINT_IN_SIZE); // First transmission size Length -= CharsEachTx; // Adjust the rest of transmission size while (CharsEachTx--) // While more chars in this chunk *AT91C_UDP_FDR2 = *pData++; // Get rid off it one by one // Pushing the data into the UDP TX-fifo *AT91C_UDP_CSR2 |= AT91C_UDP_TXPKTRDY; // Signal "DO THE TX" the stuff is delivered... while (Length) // While more bytes (I.e. packets) n total transmission { // Start filling the second bank CharsEachTx = MIN(Length, ENDPOINT_IN_SIZE); Length -= CharsEachTx; // Adjust total length while (CharsEachTx--) // While more chars in this chunk *AT91C_UDP_FDR2 = *pData++; dUsbStartTimeoutTimer(); while ( !((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP) ) // Wait for the the first bank to be sent if (dUsbTimedOut() || !(dUsbIsConfigured()) ) // Communication down..... Bail out return Length; // Invalid function - return job length not done (*AT91C_UDP_CSR2) &= ~(AT91C_UDP_TXCOMP); // Reset transmit interrupt flag while ((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP); // Wait until flag (H/W) is reset (*AT91C_UDP_CSR2) |= AT91C_UDP_TXPKTRDY; // We're ready to send next bank } // Loop while bytes to tx dUsbStartTimeoutTimer(); // Arm the timeout timing while ( !((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP) ) // Wait for transmission to complete if ( !(dUsbIsConfigured()) || dUsbTimedOut()) // Communication down..... Bail out return Length; // Invalid function - return job length not done (*AT91C_UDP_CSR2) &= ~(AT91C_UDP_TXCOMP); // Reset Interrupt flag while ((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP); // Wait for H/W to settle..... return Length; // Return byte count NOT x-ferred } static void dUsbSendStall(void) { (*AT91C_UDP_CSR0) |= AT91C_UDP_FORCESTALL; // Set STALL condition while ( !((*AT91C_UDP_CSR0) & AT91C_UDP_ISOERROR) ); // Wait until stall ack'ed (*AT91C_UDP_CSR0) &= ~(AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR); // Reset again while ((*AT91C_UDP_CSR0) & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)); // Wait until H/W really reset } static void dUsbSendZeroLengthPackage(void) { // Signal that buffer is ready to send (*AT91C_UDP_CSR0) |= AT91C_UDP_TXPKTRDY; dUsbStartTimeoutTimer(); // Wait for ACK handshake from host while ( !((*AT91C_UDP_CSR0) & AT91C_UDP_TXCOMP) && !dUsbTimedOut()); // Clear handshake flag (*AT91C_UDP_CSR0) &= ~(AT91C_UDP_TXCOMP); while ((*AT91C_UDP_CSR0) & AT91C_UDP_TXCOMP); } static void dUsbSendViaControl(const UBYTE *pData, ULONG Length) { ULONG BytesToTx = 0; AT91_REG Temp_Csr; UBYTE HaveToTxZeroLength = FALSE; UBYTE ZeroCouldBeNeeded = FALSE; // If the amount of data requested is more than what can be sent, a 0-length // packet may be required if (RequestedData > Length) { ZeroCouldBeNeeded = TRUE; // Exact same size would be interpreted as EOP @ host } do { // The endpoint size is 8 bytes. Limit each data phase to 8 bytes. BytesToTx = MIN(Length, 8); Length -= BytesToTx; // If this is the last data phase containing data, but the host requested // more, a 0-byte packet will be needed to terminate the data phase. if(ZeroCouldBeNeeded && (Length == 0) && (BytesToTx == 8)) { HaveToTxZeroLength = TRUE; } // Copy data to endpoint buffer while (BytesToTx--) { (*AT91C_UDP_FDR0) = *pData++; } // Signal that buffer is ready to send (*AT91C_UDP_CSR0) |= AT91C_UDP_TXPKTRDY; dUsbStartTimeoutTimer(); // Wait for ACK handshake from host do { Temp_Csr = (*AT91C_UDP_CSR0); // Return if the status phase occurs before the packet is accepted if (Temp_Csr & AT91C_UDP_RX_DATA_BK0) { // Clear the PKTRDY flag (*AT91C_UDP_CSR0) &= ~(AT91C_UDP_TXPKTRDY); // Clear the status phase flag (*AT91C_UDP_CSR0) &= ~(AT91C_UDP_RX_DATA_BK0); return; } } while (!(Temp_Csr & AT91C_UDP_TXCOMP) && !dUsbTimedOut()); // Clear handshake flag (*AT91C_UDP_CSR0) &= ~(AT91C_UDP_TXCOMP); while ((*AT91C_UDP_CSR0) & AT91C_UDP_TXCOMP); } while (Length); if(HaveToTxZeroLength) { dUsbSendZeroLengthPackage(); } dUsbStartTimeoutTimer(); // Wait for Status Phase while(!((*AT91C_UDP_CSR0) & AT91C_UDP_RX_DATA_BK0) && !dUsbTimedOut()); // Clear flag (*AT91C_UDP_CSR0) &= ~(AT91C_UDP_RX_DATA_BK0); } static void dUsbEnumerate(void) { UBYTE bmRequestType, bRequest; UWORD wValue, wIndex, wLength, wStatus; if ( !((*AT91C_UDP_CSR0) & AT91C_UDP_RXSETUP) ) // No setup package available return; // Bytes are popped from the FIFO one by one bmRequestType = *AT91C_UDP_FDR0; bRequest = *AT91C_UDP_FDR0; wValue = ((*AT91C_UDP_FDR0) & 0xFF); wValue |= ((*AT91C_UDP_FDR0) << 8); wIndex = ((*AT91C_UDP_FDR0) & 0xFF); wIndex |= ((*AT91C_UDP_FDR0) << 8); wLength = ((*AT91C_UDP_FDR0) & 0xFF); wLength |= ((*AT91C_UDP_FDR0) << 8); if (bmRequestType & 0x80) // If a DEVICE-TO-HOST request { *AT91C_UDP_CSR0 |= AT91C_UDP_DIR; // Enables data IN transaction in the control data stage while ( !((*AT91C_UDP_CSR0) & AT91C_UDP_DIR) ); // Repeat until the DIR bit is set } *AT91C_UDP_CSR0 &= ~AT91C_UDP_RXSETUP; // Device firmware has read the setup data in FIFO while ( ((*AT91C_UDP_CSR0) & AT91C_UDP_RXSETUP) ); // Wait until bit cleared // Handle supported standard device request from Table 9-3 in USB specification Rev 2.0 switch ((bRequest << 8) | bmRequestType) { case STD_GET_DESCRIPTOR: RequestedData = wLength; if (wValue == 0x100) // Return Device Descriptor { if (sizeof(DeviceDescriptor) > wLength) { dUsbSendViaControl(DeviceDescriptor, wLength); } else { dUsbSendViaControl(DeviceDescriptor, sizeof(DeviceDescriptor)); } } else if (wValue == 0x200) // Return Configuration Descriptor { if (sizeof(ConfigurationDescriptor) > wLength) { dUsbSendViaControl(ConfigurationDescriptor, wLength); } else { dUsbSendViaControl(ConfigurationDescriptor, sizeof(ConfigurationDescriptor)); } } else if ((wValue & 0xF00) == 0x300) { switch(wValue & 0xFF) { case 0x00: if ((sizeof(LangIdDescriptor)) > wLength) { dUsbSendViaControl(LangIdDescriptor, wLength); } else { dUsbSendViaControl(LangIdDescriptor, sizeof(LangIdDescriptor)); } break; case 0x01: if ((sizeof(SerialNumberDescriptor)) > wLength) { dUsbSendViaControl(SerialNumberDescriptor, wLength); } else { dUsbSendViaControl(SerialNumberDescriptor, sizeof(SerialNumberDescriptor)); } break; default: dUsbSendStall(); // Illegal request :-( break; } } else dUsbSendStall(); // Illegal request :-( break; case STD_SET_ADDRESS: // Status IN transfer (*AT91C_UDP_CSR0) |= AT91C_UDP_TXPKTRDY; dUsbStartTimeoutTimer(); while((*AT91C_UDP_CSR0) & AT91C_UDP_TXPKTRDY && !dUsbTimedOut()); *AT91C_UDP_FADDR = (AT91C_UDP_FEN | wValue); // Set device address. No check for invalid address. // Function endpoint enabled. *AT91C_UDP_GLBSTATE = (wValue) ? AT91C_UDP_FADDEN : 0; // If Device address != 0 then flag device // in ADDRESS STATE break; case STD_SET_CONFIGURATION: CurrentConfiguration = wValue; // Low byte of wValue = wanted configuration UsbConnectionStates = USB_CONFIGURED; dUsbSendZeroLengthPackage(); // Signal request processed OK *AT91C_UDP_GLBSTATE = (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN; // If wanted configuration != 0 *AT91C_UDP_CSR1 = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : 0; // Endpoint 1 enabled and set as BULK OUT *AT91C_UDP_CSR2 = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; // Endpoint 2 enabled and set as BULK IN *AT91C_UDP_CSR3 = (wValue) ? (AT91C_UDP_EPTYPE_INT_IN) : 0; // Endpoint 3 disabled and set as INTERRUPT IN break; case STD_GET_CONFIGURATION: // The actual configuration value is sent to HOST RequestedData = sizeof(CurrentConfiguration); dUsbSendViaControl((UBYTE *) &(CurrentConfiguration), sizeof(CurrentConfiguration)); break; case STD_GET_STATUS_ZERO: wStatus = 0x01; // Atmel has a 0x00, but we're not BUS-powered RequestedData = sizeof(wStatus); dUsbSendViaControl((UBYTE *) &wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_INTERFACE: // Everything reset to zero (reserved) wStatus = 0; RequestedData = sizeof(wStatus); dUsbSendViaControl((UBYTE *) &wStatus, sizeof(wStatus)); break; case STD_GET_STATUS_ENDPOINT: wStatus = 0; RequestedData = sizeof(wStatus); wIndex &= 0x0F; // Mask the endpoint # if (((*AT91C_UDP_GLBSTATE) & AT91C_UDP_CONFG) && (wIndex <= 3)) // If device in CONFIGURED state { // and ENDPOINT selected in valid range switch (wIndex) { case 1: wStatus = ((*AT91C_UDP_CSR1) & AT91C_UDP_EPEDS) ? 0 : 1; // If an endpoint is halted, the HALT // feature is set to 1, else reset break; case 2: wStatus = ((*AT91C_UDP_CSR2) & AT91C_UDP_EPEDS) ? 0 : 1; break; case 3: wStatus = ((*AT91C_UDP_CSR3) & AT91C_UDP_EPEDS) ? 0 : 1; break; default: // We'll never come here, but we'll never say never....... break; } dUsbSendViaControl((UBYTE *) &wStatus, sizeof(wStatus)); } else if (((*AT91C_UDP_GLBSTATE) & AT91C_UDP_FADDEN) && (wIndex == 0)) { wStatus = ((*AT91C_UDP_CSR0) & AT91C_UDP_EPEDS) ? 0 : 1; // Return 1 if device in ADRESSED state dUsbSendViaControl((UBYTE *) &wStatus, sizeof(wStatus)); } else dUsbSendStall(); // Illegal request :-( break; case STD_SET_FEATURE_ZERO: dUsbSendStall(); // Illegal request :-( break; case STD_SET_FEATURE_INTERFACE: dUsbSendZeroLengthPackage(); // TextBook break; case STD_SET_FEATURE_ENDPOINT: wIndex &= 0x0F; if ((wValue == 0) && wIndex && (wIndex <= 3)) // Feature Selector = 0 ENDPOINT HALT and { // endpoint isolated and validated switch (wIndex) { case 1: (*AT91C_UDP_CSR1) = 0; break; case 2: (*AT91C_UDP_CSR2) = 0; break; case 3: (*AT91C_UDP_CSR3) = 0; break; default: // We'll never come here, but we'll never say never....... break; } dUsbSendZeroLengthPackage(); } else dUsbSendStall(); // Illegal request :-( break; case STD_CLEAR_FEATURE_ZERO: dUsbSendStall(); // Illegal request :-( break; case STD_CLEAR_FEATURE_INTERFACE: dUsbSendZeroLengthPackage(); // No special break; case STD_CLEAR_FEATURE_ENDPOINT: wIndex &= 0x0F; if ((wValue == 0) && wIndex && (wIndex <= 3)) // Feature Selector = 0 => ENABLE A HALTED endpoint { // and endpoint isolated and validated if (wIndex == 1) (*AT91C_UDP_CSR1) = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT); // On duty again else if (wIndex == 2) (*AT91C_UDP_CSR2) = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); // - else if (wIndex == 3) (*AT91C_UDP_CSR3) = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN); // - dUsbSendZeroLengthPackage(); } else dUsbSendStall(); // Illegal request :-( break; default: dUsbSendStall(); // Illegal request :-( break; } } UBYTE dUsbIsConfigured(void) { if (*AT91C_UDP_ISR & END_OF_BUS_RESET) // If "End Of Bus Reset Interrupt" { // Somebody fallen in the wire? ;-) *AT91C_UDP_ICR = END_OF_BUS_RESET; // Reset "End Of Bus Reset Interrupt" *AT91C_UDP_ICR = SUSPEND_RESUME; // State unknown after reset, so we better clear *AT91C_UDP_ICR = WAKEUP; // As above CurrentConfiguration = 0; // We're new and ready UsbConnectionStates = USB_NOT_CONFIGURED; *AT91C_UDP_RSTEP = 0xFFFFFFFF; // Reset all implemented endpoints "and a few more" *AT91C_UDP_RSTEP = 0x0; // Restored as zeroes // Below our main crash thing, if it is missing ;-) CurrentReceiveBank = AT91C_UDP_RX_DATA_BK0; // Start the PING-PONG buffers at a known state and order *AT91C_UDP_FADDR = AT91C_UDP_FEN; // Set FEN in the Function Address Register // USB device is able to receive and transfer data *AT91C_UDP_CSR0 = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); // Configure endpoint 0 // AT91C_UDP_EPEDS = Endpoint enable // AT91C_UDP_EPTYPE_CTRL = Endpoint type CONTROL } else if (*AT91C_UDP_ISR & SUSPEND_INT) { if (UsbConnectionStates == USB_CONFIGURED) { UsbConnectionStates = USB_CONFIGURED_BUT_SUSPENDED; } else { UsbConnectionStates = USB_NOT_CONFIGURED; } *AT91C_UDP_ICR = SUSPEND_INT; CurrentReceiveBank = AT91C_UDP_RX_DATA_BK0; // Start the PING-PONG buffers at a known state and order } else if (*AT91C_UDP_ISR & SUSPEND_RESUME) { if (UsbConnectionStates == USB_CONFIGURED_BUT_SUSPENDED) { UsbConnectionStates = USB_CONFIGURED; } else { UsbConnectionStates = USB_NOT_CONFIGURED; } *AT91C_UDP_ICR = WAKEUP; *AT91C_UDP_ICR = SUSPEND_RESUME; } else if (*AT91C_UDP_ISR & AT91C_UDP_EPINT0) // If "Endpoint 0 Interrupt" { *AT91C_UDP_ICR = AT91C_UDP_EPINT0; // Reset "Endpoint 0 Interrupt" if (BrickNameKnown) dUsbEnumerate(); // Let's date & exchange "personal data" } if (UsbConnectionStates == USB_CONFIGURED) { return TRUE; } else { return FALSE; } } void dUsbInsertHandle(UBYTE Handle) { UBYTE Tmp; Tmp = 0; while((UsbHandleList[Tmp] != MAX_HANDLES) && (Tmp < MAX_HANDLES)) { Tmp++; } UsbHandleList[Tmp] = Handle; } void dUsbRemoveHandle(UBYTE Handle) { UBYTE Tmp; Tmp = 0; while (Tmp < MAX_HANDLES) { if (Handle == UsbHandleList[Tmp]) { UsbHandleList[Tmp] = MAX_HANDLES; } Tmp++; } } UWORD dUsbGetFirstHandle(void) { UWORD RtnVal; UsbHandleCnt = 0; RtnVal = dUsbGetNextHandle(); return(RtnVal); } UWORD dUsbGetNextHandle(void) { UBYTE Tmp; UWORD RtnVal; RtnVal = 0; Tmp = UsbHandleCnt; while((Tmp < MAX_HANDLES) && (MAX_HANDLES == UsbHandleList[Tmp])) { Tmp++; } UsbHandleCnt = Tmp + 1; if (Tmp < MAX_HANDLES) { RtnVal |= UsbHandleList[Tmp]; } else { RtnVal = 0x8100; } return(RtnVal); } UWORD dUsbCheckConnection(void) { UWORD ADValue; UWORD Return; Return = FALSE; USBReadADCValue(&ADValue); if (ADValue > 512) { Return = TRUE; } return(Return); } void dUsbInit(void) { UBYTE Tmp; // We could come from a SAMBA session and then we need // to "introduce ourself in a polite way for the PNP manager // We will pull the carpet and start a new session by removing // the pull up of the D+ wire BrickNameKnown = FALSE; dUsbStartTimeoutTimer(); // Let H/W settle dUsbDisconnect(); // Pull the carpet while(!USBTimedOut); // wait 1 mS. USBHwInit; // New session CurrentConfiguration = 0; // We're new born UsbConnectionStates = USB_NOT_CONFIGURED; CurrentReceiveBank = AT91C_UDP_RX_DATA_BK0; // Always start from Bank 0 RequestedData = 0; for(Tmp = 0; Tmp < MAX_HANDLES; Tmp++) { UsbHandleList[Tmp] = MAX_HANDLES; } } void dUsbResetConfig(void) { CurrentConfiguration = 0; // We've lost the connection UsbConnectionStates = USB_NOT_CONFIGURED; } void dUsbExit(void) { // USBExit; } nxt-firmware-1.29.7/src/d_usb.h000066400000000000000000000026411466344546000163100ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_usb.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_usb. $ // // Platform C // #ifndef D_USB #define D_USB //* public constants // LOW level commands #define OPENFILEWRITE 0x01 #define OPENFILEREAD 0x02 #define WRITEFILE 0x03 #define CLOSEFILE 0x04 // Low level direct command escape #define DIRECTCOMMAND 0x80 // Escape sent to the Loader #define USB_TIMEOUT 0x0BB8 // Equals approx. 1 mS. Used for recover a "broken" cable situation //* external function description void dUsbInit(void); void dUsbExit(void); ULONG dUsbRead(UBYTE *pData, ULONG Length); ULONG dUsbWrite( const UBYTE *pData, ULONG Length); UBYTE dUsbIsConfigured(void); void dUsbInsertHandle(UBYTE Handle); void dUsbRemoveHandle(UBYTE Handle); UWORD dUsbGetFirstHandle(void); UWORD dUsbGetNextHandle(void); UWORD dUsbCheckConnection(void); void dUsbResetConfig(void); void dUsbStoreBtAddress(UBYTE *pBtAddress); #endif nxt-firmware-1.29.7/src/d_usb.r000066400000000000000000000050761466344546000163270ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dkandlun $ // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: d_usb.r $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/d_usb. $ // // Platform C // #ifdef SAM7S256 #ifdef PROTOTYPE_PCB_3 #define ENABLEUsbPU *AT91C_PIOA_PER = AT91C_PIO_PA16; /* PIO allowed to control bit 16 */\ *AT91C_PIOA_OER = AT91C_PIO_PA16; /* Output pin 16 enabled */\ *AT91C_PIOA_SODR = AT91C_PIO_PA16 /* Pin 16 set = enable USB pull-up */ #endif #ifdef PROTOTYPE_PCB_4 #define ENABLEUsbPU *AT91C_PIOA_PER = AT91C_PIO_PA16; /* PIO allowed to control bit 16 */\ *AT91C_PIOA_OER = AT91C_PIO_PA16; /* Output pin 16 enabled */\ *AT91C_PIOA_CODR = AT91C_PIO_PA16 /* Pin 16 clear = enable USB pull-up */ #define DISABLEUsbPU *AT91C_PIOA_PER = AT91C_PIO_PA16; /* PIO allowed to control bit 16 */\ *AT91C_PIOA_OER = AT91C_PIO_PA16; /* Output pin 16 enabled */\ *AT91C_PIOA_SODR = AT91C_PIO_PA16 /* Pin 16 set = disable USB pull-up */ #endif #define USBHwInit *AT91C_CKGR_PLLR |= AT91C_CKGR_USBDIV_1; /* Set the PLL USB Divider (96MHz/2) */\ *AT91C_PMC_SCER = AT91C_PMC_UDP; /* WRITE-ONLY REG! Enables the 48MHz USB clock UDPCK (SysClk) */\ *AT91C_PMC_PCER = (1 << AT91C_ID_UDP); /* WRITE-ONLY REG! Enable USB clock (Peripheral Clock) */\ \ /* Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO */ \ \ /* Removed 22022006 14:20 pc ENABLEUsbPU BlueCore delay , No pull up before OK serial-no rec. from B.C.*/ static ULONG USBTimeOut; #define USBTimedOut (USB_TIMEOUT < ((((*AT91C_PITC_PIIR) & AT91C_PITC_CPIV) - USBTimeOut) & AT91C_PITC_CPIV)) #define USBGetActualTime USBTimeOut = ((*AT91C_PITC_PIIR) & AT91C_PITC_CPIV) #define USBReadADCValue(ADValue) *ADValue = *AT91C_ADC_CDR4 #define USBExit #define USBDisconnect DISABLEUsbPU #define USBConnect ENABLEUsbPU #endif #ifdef PCWIN #endif nxt-firmware-1.29.7/src/m_sched.c000066400000000000000000000031021466344546000166020ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: m_sched.c $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/m_sche $ // // Platform C // #define INCLUDE_OS #define MODULEHEADERS 32 #include "stdconst.h" #include "modules.h" #include "m_sched.h" #include "c_comm.h" #include "c_input.h" #include "c_button.h" #include "c_loader.h" #include "c_sound.h" #include "c_display.h" #include "c_lowspeed.h" #include "c_output.h" #include "c_cmd.h" #include "c_cmd.iom" #include "c_ioctrl.h" #include "c_ui.h" static const HEADER* pModuleHeaders[MODULEHEADERS] = { &cComm, &cInput, &cButton, &cDisplay, &cLoader, &cLowSpeed, &cOutput, &cSound, &cIOCtrl, &cCmd, &cUi, 0 }; void mSchedInit(void) { UWORD Tmp; Tmp = 0; while(pModuleHeaders[Tmp]) { (*pModuleHeaders[Tmp]).cInit((void*) pModuleHeaders); Tmp++; } } UBYTE mSchedCtrl(void) { UWORD Tmp; Tmp = 0; while(pModuleHeaders[Tmp]) { (*pModuleHeaders[Tmp]).cCtrl(); Tmp++; } return(((IOMAPCMD*)(pModuleHeaders[ENTRY_CMD]->pIOMap))->Awake); } void mSchedExit(void) { UWORD Tmp; Tmp = 0; while(pModuleHeaders[Tmp]) { (*pModuleHeaders[Tmp]).cExit(); Tmp++; } } nxt-firmware-1.29.7/src/m_sched.h000066400000000000000000000054401466344546000166160ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 14-11-07 12:40 $ // // Filename $Workfile:: m_sched.h $ // // Version $Revision:: 1 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/m_sche $ // // Platform C // #define APPNAME "LMS01" #define COPYRIGHTSTRING "Let's samba nxt arm in arm, (c)LEGO System A/S" #define COPYRIGHTSTRINGLENGTH 46 /* Number of bytes checked in COPYRIGHTSTRING */ #ifndef _WINDOWS #define SAM7SXX #ifdef SAM7SXX // // Platform ATMEL ARM7 // // #define OSC 48054850L #define SYSFREQ 1000 #include "sam7s256.h" #if defined (PROTOTYPE_PCB_3) || (PROTOTYPE_PCB_4) #define TSTPin AT91C_PIO_PA27 #else #define TSTPin AT91C_PIO_PA31 #endif #define TSTInit {\ *AT91C_PIOA_PER = TSTPin;\ *AT91C_PIOA_OER = TSTPin;\ } #define TSTOn {\ *AT91C_PIOA_SODR = TSTPin;\ } #define TSTOff {\ *AT91C_PIOA_CODR = TSTPin;\ } #define TSTExit {\ *AT91C_PIOA_ODR = TSTPin;\ *AT91C_PIOA_CODR = TSTPin;\ } /* Defines related to loader */ #define MAX_HANDLES 16 /* Defines related to I2c */ #define BYTES_TO_TX 8 #define BYTES_TO_RX 12 enum { NOS_OF_AVR_OUTPUTS = 4, NOS_OF_AVR_BTNS = 4, NOS_OF_AVR_INPUTS = 4 }; typedef struct { UWORD AdValue[NOS_OF_AVR_INPUTS]; UWORD Buttons; UWORD Battery; }IOFROMAVR; typedef struct { UBYTE Power; UBYTE PwmFreq; SBYTE PwmValue[NOS_OF_AVR_OUTPUTS]; UBYTE OutputMode; UBYTE InputPower; }IOTOAVR; extern IOTOAVR IoToAvr; extern IOFROMAVR IoFromAvr; #ifdef INCLUDE_OS #include "sam7s256.c" IOTOAVR IoToAvr; IOFROMAVR IoFromAvr; #endif #endif #else // // Platform PCWIN // // #define OSC 1192000L #define SYSFREQ 1000 #include "Pcwin.h" #ifdef INCLUDE_OS #include "Pcwin.c" #endif #endif nxt-firmware-1.29.7/src/modules.h000066400000000000000000000203001466344546000166540ustar00rootroot00000000000000// // Programmer // // Date init 14.12.2004 // // Reviser $Author:: Dktochpe $ // // Revision date $Date:: 19-02-08 8:15 $ // // Filename $Workfile:: modules.h $ // // Version $Revision:: 4 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Source/module $ // // Platform C // #ifndef MODULE_HEADER #define MODULE_HEADER #define FILENAME_LENGTH 19 // zero termination not included #define FILEHEADER_LENGTH 8 // all simple file headers #define DISPLAYLINE_LENGTH 16 // zero termination not included #define ON_BRICK_PROGRAMSTEPS 5 // no of on brick program steps #define STATUSTEXT_SIZE 8 // zero termination not included #define TXT_SOUND_EXT "rso" // Sound filename extension #define TXT_LMS_EXT "rxe" // Mindstorms program filename extension #define TXT_NXT_EXT "rpg" // Program filename extension #define TXT_TRYME_EXT "rtm" // Try me program filename extension #define TXT_DATA_EXT "log" // Datalog filename extension #define TXT_SYS_EXT "sys" // System filename extension (hidden) #define TXT_TMP_EXT "tmp" // Temporary filename extension (hidden) /* Error codes from then Loader */ enum { SUCCESS = 0x0000, INPROGRESS = 0x0001, REQPIN = 0x0002, NOMOREHANDLES = 0x8100, NOSPACE = 0x8200, NOMOREFILES = 0x8300, EOFEXSPECTED = 0x8400, ENDOFFILE = 0x8500, NOTLINEARFILE = 0x8600, FILENOTFOUND = 0x8700, HANDLEALREADYCLOSED = 0x8800, NOLINEARSPACE = 0x8900, UNDEFINEDERROR = 0x8A00, FILEISBUSY = 0x8B00, NOWRITEBUFFERS = 0x8C00, APPENDNOTPOSSIBLE = 0x8D00, FILEISFULL = 0x8E00, FILEEXISTS = 0x8F00, MODULENOTFOUND = 0x9000, OUTOFBOUNDERY = 0x9100, ILLEGALFILENAME = 0x9200, ILLEGALHANDLE = 0x9300, BTBUSY = 0x9400, BTCONNECTFAIL = 0x9500, BTTIMEOUT = 0x9600, FILETX_TIMEOUT = 0x9700, FILETX_DSTEXISTS = 0x9800, FILETX_SRCMISSING = 0x9900, FILETX_STREAMERROR = 0x9A00, FILETX_CLOSEERROR = 0x9B00 }; /* interface between comm and BC4 */ enum { MSG_BEGIN_INQUIRY, MSG_CANCEL_INQUIRY, MSG_CONNECT, MSG_OPEN_PORT, MSG_LOOKUP_NAME, MSG_ADD_DEVICE, MSG_REMOVE_DEVICE, MSG_DUMP_LIST, MSG_CLOSE_CONNECTION, MSG_ACCEPT_CONNECTION, MSG_PIN_CODE, MSG_OPEN_STREAM, MSG_START_HEART, MSG_HEARTBEAT, MSG_INQUIRY_RUNNING, MSG_INQUIRY_RESULT, MSG_INQUIRY_STOPPED, MSG_LOOKUP_NAME_RESULT, MSG_LOOKUP_NAME_FAILURE, MSG_CONNECT_RESULT, MSG_RESET_INDICATION, MSG_REQUEST_PIN_CODE, MSG_REQUEST_CONNECTION, MSG_LIST_RESULT, MSG_LIST_ITEM, MSG_LIST_DUMP_STOPPED, MSG_CLOSE_CONNECTION_RESULT, MSG_PORT_OPEN_RESULT, MSG_SET_DISCOVERABLE, MSG_CLOSE_PORT, MSG_CLOSE_PORT_RESULT, MSG_PIN_CODE_ACK, MSG_DISCOVERABLE_ACK, MSG_SET_FRIENDLY_NAME, MSG_SET_FRIENDLY_NAME_ACK, MSG_GET_LINK_QUALITY, MSG_LINK_QUALITY_RESULT, MSG_SET_FACTORY_SETTINGS, MSG_SET_FACTORY_SETTINGS_ACK, MSG_GET_LOCAL_ADDR, MSG_GET_LOCAL_ADDR_RESULT, MSG_GET_FRIENDLY_NAME, MSG_GET_DISCOVERABLE, MSG_GET_PORT_OPEN, MSG_GET_FRIENDLY_NAME_RESULT, MSG_GET_DISCOVERABLE_RESULT, MSG_GET_PORT_OPEN_RESULT, MSG_GET_VERSION, MSG_GET_VERSION_RESULT, MSG_GET_BRICK_STATUSBYTE_RESULT, MSG_SET_BRICK_STATUSBYTE_RESULT, MSG_GET_BRICK_STATUSBYTE, MSG_SET_BRICK_STATUSBYTE }; #define SIZE_OF_BT_NAME 16 #define SIZE_OF_BRICK_NAME 8 #define SIZE_OF_CLASS_OF_DEVICE 4 #define SIZE_OF_BT_PINCODE 16 #define SIZE_OF_BDADDR 7 enum { ENTRY_COMM, ENTRY_INPUT, ENTRY_BUTTON, ENTRY_DISPLAY, ENTRY_LOADER, ENTRY_LOWSPEED, ENTRY_OUTPUT, ENTRY_SOUND, ENTRY_IOCTRL, ENTRY_CMD, ENTRY_UI, ENTRY_FREE2, ENTRY_FREE3, ENTRY_FREE4, ENTRY_FREE5 }; typedef struct { ULONG ModuleID; UBYTE ModuleName[FILENAME_LENGTH + 1]; void (*cInit)(void* pHeader); void (*cCtrl)(void); void (*cExit)(void); void *pIOMap; void *pVars; UWORD IOMapSize; UWORD VarsSize; UWORD ModuleSize; }HEADER; enum { FILEFORMAT_SOUND = 0x0100, // rso FILEFORMAT_SOUND_COMPRESSED = 0x0101, // rso FILEFORMAT_BITMAP = 0x0200, FILEFORMAT_FONT = 0x0300, FILEFORMAT_ICON = 0x0400, FILEFORMAT_TEXT = 0x0500, FILEFORMAT_MELODY = 0x0600, FILEFORMAT_MENU = 0x0700, // rms FILEFORMAT_PROGRAM = 0x0800, // rpg FILEFORMAT_DATALOG = 0x0900 // rdt }; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DateBytesMsb; UBYTE DataBytesLsb; UBYTE SampleRateMsb; UBYTE SampleRateLsb; UBYTE PlayModeMsb; UBYTE PlayModeLsb; UBYTE Data[]; } SOUND; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DateBytesMsb; UBYTE DataBytesLsb; UBYTE StartX; UBYTE StartY; UBYTE PixelsX; UBYTE PixelsY; UBYTE Data[]; } BMPMAP; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DataBytesMsb; UBYTE DataBytesLsb; UBYTE ItemsX; UBYTE ItemsY; UBYTE ItemPixelsX; UBYTE ItemPixelsY; UBYTE Data[]; } FONT; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DataBytesMsb; UBYTE DataBytesLsb; UBYTE ItemsX; UBYTE ItemsY; UBYTE ItemPixelsX; UBYTE ItemPixelsY; UBYTE Data[]; } ICON; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DateBytesMsb; UBYTE DataBytesLsb; UBYTE TonesMsb; UBYTE TonesLsb; UBYTE PlayModeMsb; UBYTE PlayModeLsb; UBYTE Data[]; // Data[0] = FreqMsb, Data[1] = FreqLsb, Data[2] = DurationMsb, Data[3] = DurationLsb .... } MELODY; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DataBytesMsb; UBYTE DataBytesLsb; UBYTE Steps; UBYTE NotUsed1; UBYTE NotUsed2; UBYTE NotUsed3; UBYTE Data[]; } PROGRAM; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DataBytesMsb; UBYTE DataBytesLsb; UBYTE TotalTime3; UBYTE TotalTime2; UBYTE TotalTime1; UBYTE TotalTime0; UBYTE Data[]; } DATALOG; #define ICON_TEXTLNG 15 // 15 characters #define ICON_IMAGESIZE 72 // 24 x 24 pixels #define MAX_MENUITEMS 256 typedef struct { UBYTE ItemId67; // Menu item id UBYTE ItemId45; // Menu item id UBYTE ItemId23; // Menu item id UBYTE ItemId01; // Menu item id UBYTE SpecialMask3; // Menu item special mask (TBD) UBYTE SpecialMask2; // Menu item special mask (TBD) UBYTE SpecialMask1; // Menu item special mask (TBD) UBYTE SpecialMask0; // Menu item special mask (TBD) UBYTE FunctionIndex; // Menu item enter function call index UBYTE FunctionParameter; // Menu item enter function parameter UBYTE FileLoadNo; // Menu item enter menu file load no UBYTE NextMenu; // Menu item enter next level menu no UBYTE IconText[ICON_TEXTLNG + 1]; // Menu item icon text string UBYTE IconImageNo; // Menu item icon image number }MENUITEM; typedef struct { UBYTE FormatMsb; UBYTE FormatLsb; UBYTE DataBytesMsb; UBYTE DataBytesLsb; UBYTE ItemSize; UBYTE Items; UBYTE ItemPixelsX; UBYTE ItemPixelsY; MENUITEM Data[MAX_MENUITEMS]; } MENU; typedef UBYTE (*FUNCTION)(UBYTE); // Menu function type #endif nxt-firmware-1.29.7/src/sam7s256.c000066400000000000000000000012711466344546000164740ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 10-12-07 14:29 $ // // Filename $Workfile:: sam7s256.c $ // // Version $Revision:: 3 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Sam7s256/Incl $ // // Platform C // #ifdef ARMDEBUG #include "debug_stub.h" #endif int main(void) { while(TRUE) { HARDWAREInit; mSchedInit(); #ifdef ARMDEBUG dbg__bkpt_init(); #endif while(TRUE == mSchedCtrl()) { OSWatchdogWrite; } mSchedExit(); HARDWAREExit; } } nxt-firmware-1.29.7/src/sam7s256.h000066400000000000000000000046041466344546000165040ustar00rootroot00000000000000// // Date init 14.12.2004 // // Revision date $Date:: 24-04-08 14:33 $ // // Filename $Workfile:: sam7s256.h $ // // Version $Revision:: 5 $ // // Archive $Archive:: /LMS2006/Sys01/Main_V02/Firmware/Sam7s256/Incl $ // // Platform C // #ifndef SAM7S256_H #define SAM7S256_H #ifdef __IAR_SYSTEMS_ICC__ #include "ioat91sam7s256.h" #else #include "AT91SAM7S256.h" #endif #define SAM7S256 #define HARDWAREInit {\ ULONG TmpReset;\ *AT91C_RSTC_RMR = 0xA5000401;\ *AT91C_AIC_DCR = 1;\ *AT91C_PITC_PIMR = (0x000FFFFF | 0x01000000);\ TmpReset = *AT91C_PITC_PIVR;\ TmpReset = TmpReset;/* Suppress warning*/\ *AT91C_PMC_PCER = (1L< .data" .section .vectmapped, "ax" .else .print "Vectors in section .vectorg -> .text" .section .vectorg, "ax" .endif LDR PC,Reset_Addr /* 0x00 Reset handler */ LDR PC,Undef_Addr /* 0x04 Undefined Instruction */ LDR PC,SWI_Addr /* 0x08 Software Interrupt */ LDR PC,PAbt_Addr /* 0x0C Prefetch Abort */ LDR PC,DAbt_Addr /* 0x10 Data Abort */ NOP /* 0x14 reserved */ LDR PC,IRQ_Addr /* 0x18 IRQ */ fiqvec: /* 0x1c FIQ */ /*------------------------------------------------------------------------------ //*- Function : FIQ_Handler_Entry //*- Treatments : FIQ Controller Interrupt Handler. //*- Called Functions : AIC_FVR[interrupt] //*------------------------------------------------------------------------------*/ FIQ_Handler_Entry: /*- Switch in SVC/User Mode to allow User Stack access for C code */ /* because the FIQ is not yet acknowledged*/ /*- Save and r0 in FIQ_Register */ mov r9,r0 ldr r0 , [r8, #AIC_FVR] msr CPSR_c,#I_BIT | F_BIT | ARM_MODE_SVC /*- Save scratch/used registers and LR in User Stack */ stmfd sp!, { r1-r3, r12, lr} /*- Branch to the routine pointed by the AIC_FVR */ mov r14, pc bx r0 /*- Restore scratch/used registers and LR from User Stack */ ldmia sp!, { r1-r3, r12, lr} /*- Leave Interrupts disabled and switch back in FIQ mode */ msr CPSR_c, #I_BIT | F_BIT | ARM_MODE_FIQ /*- Restore the R0 ARM_MODE_SVC register */ mov r0,r9 /*- Restore the Program Counter using the LR_fiq directly in the PC */ subs pc,lr,#4 /* end of fiqhandler */ Reset_Addr: .word InitReset #ifdef ARMDEBUG Undef_Addr: .word undef_handler /* BKPT instruction trap */ #else Undef_Addr: .word Undef_Handler #endif SWI_Addr: .word SWI_Handler /*SWI_Addr: .word SoftwareInterruptASM*/ /*in swi_handler.S */ #ifdef ARMDEBUG PAbt_Addr: .word prefetch_abort_handler DAbt_Addr: .word data_abort_handler #else PAbt_Addr: .word PAbt_Handler DAbt_Addr: .word DAbt_Handler #endif IRQ_Addr: .word IRQ_Handler_Entry .global default_undef_handler default_undef_handler: Undef_Handler: B Undef_Handler SWI_Handler: B SWI_Handler .global default_prefetch_abort_handler default_prefetch_abort_handler: PAbt_Handler: B PAbt_Handler .global default_data_abort_handler default_data_abort_handler: DAbt_Handler: B DAbt_Handler .arm .section .init, "ax" .global _startup .func _startup _startup: reset: .if (VECTREMAPPED) /* mthomas: Dummy used during startup */ LDR PC, Reset_Addr_F NOP NOP NOP NOP NOP /*.word 0xdeadbeef*/ /* Reserved Address */ NOP NOP Reset_Addr_F: .word InitReset .endif .RAM_TOP: .word __TOP_STACK InitReset: /*------------------------------------------------------------------------------ /*- Remapping /*------------------------------------------------------------------------------*/ .if (VECTREMAPPED) .print "RCR setting for remapping enabled" .equ MC_BASE,0xFFFFFF00 /* MC Base Address */ .equ MC_RCR, 0x00 /* MC_RCR Offset */ .if (VECTREMAPPED_AUTODETECT) /* store first word in RAM into r4 */ ldr r0,=__FIRST_IN_RAM ldr r4,[r0] /* load value at address 0 into R2 */ ldr r1,=0x00000000 ldr r2,[r1] /* xor value from address 0 (flip all bits), store in R3 */ ldr r3,=0xffffffff eor r3, r2, r3 /* write xored value to first word in RAM if already remapped this will also change the value at 0 */ str r3,[r0] /* load from address 0 again into R3 */ ldr r3,[r1] /* restore first value in RAM */ str r4,[r0] /* compare */ cmp r3, r2 bne already_remapped .endif /* if both values have been equal the change of the RAM-value had no effect on the value at 0x00000000 so we are not remapping yet -> remap now: */ LDR R0, =MC_BASE MOV R1, #1 STR R1, [R0, #MC_RCR] already_remapped: .endif /*------------------------------------------------------------------------------ /*- Low level Init (PMC, AIC, ? ....) by C function AT91F_LowLevelInit /*------------------------------------------------------------------------------*/ .extern AT91F_LowLevelInit /*- minumum C initialization */ /*- call AT91F_LowLevelInit( void) */ ldr sp, .RAM_TOP /* temporary stack in internal RAM (**) */ /*--Call Low level init function in ABSOLUTE through the Interworking */ ldr r0,=AT91F_LowLevelInit mov lr, pc bx r0 /*------------------------------------------------------------------------------ //*- Stack Sizes Definition //*------------------------ //*- Interrupt Stack requires 2 words x 8 priority level x 4 bytes when using //*- the vectoring. This assume that the IRQ management. //*- The Interrupt Stack must be adjusted depending on the interrupt handlers. //*- Fast Interrupt not requires stack If in your application it required you must //*- be definehere. //*- The System stack size is not defined and is limited by the free internal //*- SRAM. //*------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------ //*- Top of Stack Definition //*------------------------- //*- Interrupt and Supervisor Stack are located at the top of internal memory in //*- order to speed the exception handling context saving and restoring. //*- ARM_MODE_SVC (Application, C) Stack is located at the top of the external memory. //*------------------------------------------------------------------------------*/ .EQU IRQ_STACK_SIZE, (3*8*4) .EQU FIQ_STACK_SIZE, (3*8*4) .EQU ARM_MODE_FIQ, 0x11 .EQU ARM_MODE_IRQ, 0x12 .EQU ARM_MODE_SVC, 0x13 .EQU ARM_MODE_ABT, 0x17 .EQU I_BIT, 0x80 .EQU F_BIT, 0x40 /*------------------------------------------------------------------------------ //*- Setup the stack for each mode //*-------------------------------*/ mov r0, sp /* see (**) */ #ifdef ARMDEBUG /*- Set up Abort Mode Stack for Debugger*/ msr CPSR_c, #ARM_MODE_ABT | I_BIT | F_BIT ldr sp, =__abort_stack_top__ #endif /*- Set up Fast Interrupt Mode and set FIQ Mode Stack*/ msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT mov sp, r0 sub r0, r0, #FIQ_STACK_SIZE /*- Init the FIQ register*/ ldr r8, =AT91C_BASE_AIC /*- Set up Interrupt Mode and set IRQ Mode Stack*/ msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT mov sp, r0 /* Init stack IRQ */ sub r0, r0, #IRQ_STACK_SIZE /*- Set up Supervisor Mode and set Supervisor Mode Stack*/ // /* start with INT and FIQ enabled */ msr CPSR_c, #ARM_MODE_SVC /* start with INT and FIQ disabled */ // msr CPSR_c, #ARM_MODE_SVC | I_BIT | F_BIT mov sp, r0 /* Init stack Sup */ /*- Enable interrupt & Set up Supervisor Mode and set Supervisor Mode Stack*/ /* Relocate .data section (Copy from ROM to RAM) This will also copy the .vectmapped and .fastrun */ LDR R1, =_etext LDR R2, =_data LDR R3, =_edata LoopRel: CMP R2, R3 LDRLO R0, [R1], #4 STRLO R0, [R2], #4 BLO LoopRel /* Clear .bss section (Zero init) */ MOV R0, #0 LDR R1, =__bss_start__ LDR R2, =__bss_end__ LoopZI: CMP R1, R2 STRLO R0, [R1], #4 BLO LoopZI .if (CPP_CONSTRUCTORS) /* call C++ constructors of global objects */ LDR r0, =__ctors_start__ LDR r1, =__ctors_end__ ctor_loop: CMP r0, r1 BEQ ctor_end LDR r2, [r0], #4 STMFD sp!, {r0-r1} MOV lr, pc /* MOV pc, r2 */ BX r2 /* mthomas 8/2006 */ LDMFD sp!, {r0-r1} B ctor_loop ctor_end: .endif /* call main() */ ldr lr,=exit ldr r0,=main bx r0 .size _startup, . - _startup .endfunc /* "exit" dummy added by mthomas to avoid sbrk write read etc. needed by the newlib default "exit" */ .global exit .func exit exit: b . .size exit, . - exit .endfunc /*------------------------------------------------------------------------------ //*- Manage exception //*--------------- //*- This module The exception must be ensure in ARM mode //*------------------------------------------------------------------------------ //*------------------------------------------------------------------------------ //*- Function : IRQ_Handler_Entry //*- Treatments : IRQ Controller Interrupt Handler. //*- Called Functions : AIC_IVR[interrupt] //*------------------------------------------------------------------------------*/ .if (VECTREMAPPED) .print "IRQ_Handler_Entry in section .fastrun -> .data" .section .fastrun, "ax" .else .print "IRQ_Handler_Entry in section .init -> .text" .section .init, "ax" .endif .global IRQ_Handler_Entry .func IRQ_Handler_Entry IRQ_Handler_Entry: /*---- Adjust and save return address on the stack */ sub lr, lr, #4 stmfd sp!, {lr} /*---- Save r0 and SPSR on the stack */ mrs r14, SPSR stmfd sp!, {r0, r14} /*---- Write in the IVR to support Protect mode */ /*---- No effect in Normal Mode */ /*---- De-assert NIRQ and clear the source in Protect mode */ ldr r14, =AT91C_BASE_AIC ldr r0, [r14, #AIC_IVR] str r14, [r14, #AIC_IVR] /*---- Enable nested interrupts and switch to Supervisor mode */ msr CPSR_c, #ARM_MODE_SVC /*---- Save scratch/used registers and LR on the stack */ stmfd sp!, {r1-r3, r12, r14} /*---- Branch to the routine pointed by AIC_IVR */ mov r14, pc bx r0 /*---- Restore scratch/used registers and LR from the stack */ ldmia sp!, {r1-r3, r12, r14} /*---- Disable nested interrupts and switch back to IRQ mode */ msr CPSR_c, #I_BIT | ARM_MODE_IRQ /*---- Acknowledge interrupt by writing AIC_EOICR */ ldr r14, =AT91C_BASE_AIC str r14, [r14, #AIC_EOICR] /*---- Restore SPSR and r0 from the stack */ ldmia sp!, {r0, r14} msr SPSR_cxsf, r14 /*---- Return from interrupt handler */ ldmia sp!, {pc}^ .size IRQ_Handler_Entry, . - IRQ_Handler_Entry .endfunc /*--------------------------------------------------------------- //* ?EXEPTION_VECTOR //* This module is only linked if needed for closing files. //*---------------------------------------------------------------*/ .global AT91F_Default_FIQ_handler .func AT91F_Default_FIQ_handler AT91F_Default_FIQ_handler: b AT91F_Default_FIQ_handler .size AT91F_Default_FIQ_handler, . - AT91F_Default_FIQ_handler .endfunc .global AT91F_Default_IRQ_handler .func AT91F_Default_IRQ_handler AT91F_Default_IRQ_handler: b AT91F_Default_IRQ_handler .size AT91F_Default_IRQ_handler, . - AT91F_Default_IRQ_handler .endfunc .global AT91F_Spurious_handler .func AT91F_Spurious_handler AT91F_Spurious_handler: b AT91F_Spurious_handler .size AT91F_Spurious_handler, . - AT91F_Spurious_handler .endfunc /*------------------------------------------------------------------------------ //*- Various debugger stacks. //*-------------------------------*/ #ifdef ARMDEBUG .section .stack.abort, "aw", %nobits .space 0x80; /* 128 byte abort mode stack. */ .section .stack.debugger, "aw", %nobits .space 0x48; /* 16 user mode registers + SPSR + UNDEF Next Instruction Address */ .section .breakpoints, "aw", %nobits .space 0x40; /* Single Stepping Breakpoint + 7 Breakpoints */ #endif .end nxt-firmware-1.29.7/startup/Cstartup_SAM7.c000066400000000000000000000072161466344546000205210ustar00rootroot00000000000000//*---------------------------------------------------------------------------- //* ATMEL Microcontroller Software Support - ROUSSET - //*---------------------------------------------------------------------------- //* The software is delivered "AS IS" without warranty or condition of any //* kind, either express, implied or statutory. This includes without //* limitation any warranty or condition with respect to merchantability or //* fitness for any particular purpose, or against the infringements of //* intellectual property rights of others. //*---------------------------------------------------------------------------- //* File Name : Cstartup_SAM7.c //* Object : Low level initializations written in C for IAR Tools //* Creation : 12/Jun/04 //* 1.2 28/Feb/05 JPP : LIB change AT91C_WDTC_WDDIS & PLL //* 1.3 21/Mar/05 JPP : Change PLL Wait time //*---------------------------------------------------------------------------- // Include the board file description #include "AT91SAM7S256.h" // The following functions must be write in ARM mode this function called directly // by exception vector extern void AT91F_Spurious_handler(void); extern void AT91F_Default_IRQ_handler(void); extern void AT91F_Default_FIQ_handler(void); #ifdef __IAR_SYSTEMS_ICC__ # define SECTION_ICODE @ "ICODE" #else # define SECTION_ICODE #endif //*---------------------------------------------------------------------------- //* \fn AT91F_LowLevelInit //* \brief This function performs very low level HW initialization //* this function can be use a Stack, depending the compilation //* optimization mode //*---------------------------------------------------------------------------- void AT91F_LowLevelInit( void) SECTION_ICODE { int i; AT91PS_PMC pPMC = AT91C_BASE_PMC; //* Set Flash Waite sate // Single Cycle Access at Up to 30 MHz, or 40 // if MCK = 47923200 I have 72 Cycle for 1,5 usecond ( flied MC_FMR->FMCN AT91C_BASE_MC->MC_FMR = ((AT91C_MC_FMCN)&(72 <<16)) | AT91C_MC_FWS_1FWS ; //* Watchdog Disable AT91C_BASE_WDTC->WDTC_WDMR= AT91C_WDTC_WDDIS; //* Set MCK at 47 923 200 // 1 Enabling the Main Oscillator: // SCK = 1/32768 = 30.51 uSecond // Start up time = 8 * 6 / SCK = 56 * 30.51 = 1,46484375 ms pPMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); // Wait the startup time while(!(pPMC->PMC_SR & AT91C_PMC_MOSCS)); // 2 Checking the Main Oscillator Frequency (Optional) // 3 Setting PLL and divider: // - div by 14 Fin = 1.3165 =(18,432 / 14) // - Mul 72+1: Fout = 96.1097 =(3,6864 *73) // for 96 MHz the erroe is 0.11% // Field out NOT USED = 0 // PLLCOUNT pll startup time estimate at : 0.844 ms // PLLCOUNT 28 = 0.000844 /(1/32768) pPMC->PMC_PLLR = ((AT91C_CKGR_DIV & 14) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (72<<16))); // Wait the startup time while(!(pPMC->PMC_SR & AT91C_PMC_LOCK)); while(!(pPMC->PMC_SR & AT91C_PMC_MCKRDY)); // 4. Selection of Master Clock and Processor Clock // select the PLL clock divided by 2 pPMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2 ; while(!(pPMC->PMC_SR & AT91C_PMC_MCKRDY)); pPMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while(!(pPMC->PMC_SR & AT91C_PMC_MCKRDY)); // Set up the default interrupts handler vectors AT91C_BASE_AIC->AIC_SVR[0] = (int) AT91F_Default_FIQ_handler ; for (i=1;i < 31; i++) { AT91C_BASE_AIC->AIC_SVR[i] = (int) AT91F_Default_IRQ_handler ; } AT91C_BASE_AIC->AIC_SPU = (int) AT91F_Spurious_handler ; } nxt-firmware-1.29.7/tests/000077500000000000000000000000001466344546000154135ustar00rootroot00000000000000nxt-firmware-1.29.7/tests/.gitignore000066400000000000000000000000231466344546000173760ustar00rootroot00000000000000*.rxe *.out *.diff nxt-firmware-1.29.7/tests/Makefile000066400000000000000000000010501466344546000170470ustar00rootroot00000000000000NBC = nbc OPTIMIZE = -Z2 FIRMWARE = PYTHON = python3 # or PYTHON = poetry -C path/to/nxt-python run python TESTS = float tests: $(TESTS:%=%.diff) build: $(TESTS:%=%.rxe) %.rxe: %.nxc $(NBC) $(OPTIMIZE) $(FIRMWARE) -O=$@ $< %.load: %.rxe $(NBC) $(OPTIMIZE) $(FIRMWARE) -d -b $< %.out: %.load $(PYTHON) term.py -qos $* > $@ %.diff: %.out %.expect diff $^ > $@ %.S: %.nxc $(NBC) $(OPTIMIZE) $(FIRMWARE) -nbc=$@ $< clean-out: rm -f $(TESTS:%=%.out) $(TESTS:%=%.diff) clean: clean-out rm -f $(TESTS:%=%.rxe) .SECONDARY: .DELETE_ON_ERROR: nxt-firmware-1.29.7/tests/float.expect000066400000000000000000000014661466344546000177410ustar00rootroot00000000000000move -12.9 char -12 244 short -12 65524 long -12 4294967284 move -12.34 char -12 244 short -12 65524 long -12 4294967284 move 12.34 char 12 12 short 12 12 long 12 12 move 12.9 char 12 12 short 12 12 long 12 12 move[] -12.9 char -12 244 short -12 65524 long -12 4294967284 move[] -12.34 char -12 244 short -12 65524 long -12 4294967284 move[] 12.34 char 12 12 short 12 12 long 12 12 move[] 12.9 char 12 12 short 12 12 long 12 12 neg -12.9 char 12 12 short 12 12 long 12 12 neg -12.34 char 12 12 short 12 12 long 12 12 neg 12.34 char -12 244 short -12 65524 long -12 4294967284 neg 12.9 char -12 244 short -12 65524 long -12 4294967284 out -12.9 char -13 243 out -12.34 char -12 244 out 12.34 char 12 12 out 12.9 char 13 13 nxt-firmware-1.29.7/tests/float.nxc000066400000000000000000000111611466344546000172320ustar00rootroot00000000000000/* * Copyright (C) 2024 Nicolas Schodet * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * Convert floating point values to integer, check conversion is right. * * Actual check is done on the attached computer, see float.expect. * * When converting a float to an integer, if the integer is not able to * represent the value, the behavior is implementation defined. * * The original firmware source code depends on this implementation defined * behavior from the C compiler. * * The LEGO MINDSTORMS NXT Executable File Specification does not define what * is the expected behavior for the firmware because it was released for * version 1.03, before float was supported. However it says: "At the scalar * level, data type conversions behave identically to type casts in ANSI C.". * * For NXT Improved firmware, I decided to follow the behavior of the original * firmware binary, which is: * * - Negative values to unsigned numbers: large positive integer (two's * complement). * - Round to nearest for SETOUT instruction, truncate for other operations. * * There are so many code paths inside the firmware to do the same thing that * I may have missed some other cases. If you find such cases, please update * this test. */ #include "term.h" task main() { float a[4] = {-12.9, -12.34, 12.34, 12.9}; int i; Wait(1000); /* * Test conversion using MOV instruction. */ for (i = 0; i < 4; i++) { float af = a[i]; char ac = af; unsigned char auc = af; short as = af; unsigned short aus = af; long al = af; unsigned long aul = af; TermText("move "); TermNumNl(af); TermText(" char "); TermNum(ac); TermText(" "); TermNumNl(auc); TermText(" short "); TermNum(as); TermText(" "); TermNumNl(aus); TermText(" long "); TermNum(al); TermText(" "); TermNumNl(aul); Wait(100); } /* * Test conversion using MOV instruction with an array. */ while (1) { char ac[4]; ac = a; unsigned char auc[4]; auc = a; short as[4]; as = a; unsigned short aus[4]; aus = a; long al[4]; al = a; unsigned long aul[4]; aul = a; for (i = 0; i < 4; i++) { TermText("move[] "); TermNumNl(a[i]); TermText(" char "); TermNum(ac[i]); TermText(" "); TermNumNl(auc[i]); TermText(" short "); TermNum(as[i]); TermText(" "); TermNumNl(aus[i]); TermText(" long "); TermNum(al[i]); TermText(" "); TermNumNl(aul[i]); Wait(100); } break; } /* * Test conversion using NEG instruction. */ for (i = 0; i < 4; i++) { float af = a[i]; char ac = -af; unsigned char auc = -af; short as = -af; unsigned short aus = -af; long al = -af; unsigned long aul = -af; TermText("neg "); TermNumNl(af); TermText(" char "); TermNum(ac); TermText(" "); TermNumNl(auc); TermText(" short "); TermNum(as); TermText(" "); TermNumNl(aus); TermText(" long "); TermNum(al); TermText(" "); TermNumNl(aul); Wait(100); } /* * Test conversion using SETOUT/GETOUT instructions. */ for (i = 0; i < 4; i++) { float af = a[i]; SetOutput(OUT_A, PowerField, af, RegPValueField, af); char ac = GetOutput(OUT_A, PowerField); unsigned char auc = GetOutput(OUT_A, RegPValueField); TermText("out "); TermNumNl(af); TermText(" char "); TermNum(ac); TermText(" "); TermNumNl(auc); Wait(100); } } nxt-firmware-1.29.7/tests/term.h000066400000000000000000000035161466344546000165400ustar00rootroot00000000000000#ifndef term_h #define term_h /* * Copyright (C) 2024 Nicolas Schodet * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Terminal like output using messages. */ int term_screen_line = LCD_LINE1; string term_line = ""; inline void TermTextSub(string s, bool nl) { term_line = StrCat(term_line, s); if (nl) { TextOut(0, term_screen_line, term_line); if (term_screen_line) term_screen_line -= 8; else term_screen_line = LCD_LINE1; ClearLine(term_screen_line); SendMessage(10, term_line); Wait(2); term_line = ""; } } #define TermText(s) TermTextSub(s, FALSE) #define TermTextNl(s) TermTextSub(s, TRUE) #define TermNum(num) TermText(NumToStr(num)) #define TermNumNl(num) TermTextNl(NumToStr(num)) #define TermNl(num) TermTextNl("") #endif nxt-firmware-1.29.7/tests/term.py000066400000000000000000000056601466344546000167430ustar00rootroot00000000000000#!/usr/bin/env python3 """Display terminal output from NXT program.""" # # Copyright (C) 2024 Nicolas Schodet # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. # import argparse import nxt.locator import nxt.error import os.path import time p = argparse.ArgumentParser(description=__doc__) p.add_argument("-s", "--start", metavar="PROGRAM", help="start program (with .rxe extension if not given") p.add_argument("-q", "--quiet", action="store_true", help="output nothing while waiting") p.add_argument("-o", "--once", action="store_true", help="exit after program end") nxt.locator.add_arguments(p) options = p.parse_args() spinner = "-\\|/" spinning = False seen = False def out(message): print(message.rstrip(b"\0").decode("windows-1252")) with nxt.locator.find_with_options(options) as b: try: if options.start: prog = options.start if not os.path.splitext(prog)[1]: prog = prog + ".rxe" b.start_program(prog) while True: try: (inbox, message) = b.message_read(10, 0, True) if spinning: print("\b", end="") out(message) seen = True except nxt.error.EmptyMailboxError as e: if spinning: print("\b", end="") if not options.quiet: print("_", end="", flush=True) spinning = True time.sleep(0.05) seen = True except nxt.error.NoActiveProgramError: if spinning: print("\b", end="") if seen and options.once: break if not options.quiet: print(spinner[0], end="", flush=True) spinner = spinner[1:] + spinner[0] spinning = True time.sleep(0.2) except KeyboardInterrupt: pass nxt-firmware-1.29.7/tools/000077500000000000000000000000001466344546000154115ustar00rootroot00000000000000nxt-firmware-1.29.7/tools/img2src000077500000000000000000000126121466344546000167070ustar00rootroot00000000000000#!/usr/bin/env python3 # """Convert image to NXT data source file.""" # # Copyright (C) 2024 Nicolas Schodet # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # import argparse import os import os.path import struct import sys import tomllib from PIL import Image, ImageChops def print_hex(indent, bytes_or_fmt, *args, wrap=8, file=None): """Print data as hexadecimal, use struct format.""" if not args: data = bytes_or_fmt else: data = struct.pack(bytes_or_fmt, *args) for i in range(0, len(data), wrap): line = ", ".join(f"{b:#04x}" for b in data[i : i + wrap]) print(indent + line + ",", file=file) def data_from_image(img): """Create data in NXT bitmap format from image. Pixels in NXT are organized as packets of 8 pixel high columns. PIL does not write this format, so just transpose the image then transpose every bytes. """ if img.height % 8 != 0: raise RuntimeError("Height must be multiple of 8") # Transpose. img = img.transpose(Image.Transpose.TRANSPOSE) # Extract data data = img.tobytes("raw", "1;IR") # Split in lines (corresponding to columns). data = [data[i : i + img.width // 8] for i in range(0, len(data), img.width // 8)] # Transpose. data = zip(*data) # Paste everything together. data = bytes(val for band in data for val in band) return data def crop_image(img): """Crop borders to reduce image size, return cropped image and x and y offsets as a tuple. """ print(img.width, img.height) imginv = ImageChops.invert(img) bbox = imginv.getbbox() if bbox is None: # Special case for Test2 image. return img, (0, 0) left, upper, right, lower = bbox print(left, upper, right, lower) # Round down to multiple of 8. upper = upper // 8 * 8 lower = (lower + 7) // 8 * 8 return img.crop((left, upper, right, lower)), (left, upper) def convert_bitmap(info, img_file, out_file, crop=False): """Convert to BMPMAP format.""" img = Image.open(img_file) if crop: img, (crop_x, crop_y) = crop_image(img) else: crop_x, crop_y = 0, 0 data = data_from_image(img) start_x = info["start_x"] start_y = info["start_y"] basename = os.path.basename(os.path.splitext(img_file)[0]) with open(out_file, "w") as f: print(f"#define {basename}_size {len(data)+8}", file=f) print(f"const BMPMAP {basename} =", file=f) print("{", file=f) print_hex(" ", ">H", 0x0200, file=f) print_hex(" ", ">H", len(data), file=f) print_hex(" ", "B", start_x + crop_x, file=f) print_hex(" ", "B", start_y + crop_y, file=f) print_hex(" ", "B", img.width, file=f) print_hex(" ", "B", img.height, file=f) print(" {", file=f) print_hex(" ", data, file=f) print(" }", file=f) print("};", file=f) def convert_icon(info, img_file, out_file): """Convert to ICON format.""" img = Image.open(img_file) data = data_from_image(img) item_pixels_x = info["item_pixels_x"] item_pixels_y = info["item_pixels_y"] basename = os.path.basename(os.path.splitext(out_file)[0]) with open(out_file, "w") as f: print(f"const ICON {basename} =", file=f) print("{", file=f) print_hex(" ", ">H", 0x0400, file=f) print_hex(" ", ">H", len(data), file=f) print_hex(" ", "B", img.width // item_pixels_x, file=f) print_hex(" ", "B", img.height // item_pixels_y, file=f) print_hex(" ", "B", item_pixels_x, file=f) print_hex(" ", "B", item_pixels_y, file=f) print(" {", file=f) print_hex(" ", data, file=f) print(" }", file=f) print("};", file=f) p = argparse.ArgumentParser(description=__doc__) p.add_argument("info", help="input TOML file") p.add_argument("image", help="input image") p.add_argument("-o", "--output", metavar="FILE", help="output header file") options = p.parse_args() try: with open(options.info, "rb") as f: info = tomllib.load(f) if info["format"] == "bitmap": convert_bitmap(info, options.image, options.output) elif info["format"] == "icon": convert_icon(info, options.image, options.output) else: raise RuntimeError("Unknown format") except Exception as e: try: os.remove(options.output) except FileNotFoundError: pass print(e, file=sys.stderr) sys.exit(1) nxt-firmware-1.29.7/tools/menu2src000077500000000000000000000141731466344546000171030ustar00rootroot00000000000000#!/usr/bin/env python3 # """Convert menu to NXT data source file.""" # # Copyright (C) 2024 Nicolas Schodet # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # import argparse import os import os.path import struct import sys import tomllib def print_hex(indent, bytes_or_fmt, *args, wrap=8, file=None): """Print data as hexadecimal, use struct format.""" if not args: data = bytes_or_fmt else: data = struct.pack(bytes_or_fmt, *args) for i in range(0, len(data), wrap): line = ", ".join(f"{b:#04x}" for b in data[i : i + wrap]) print(indent + line + ",", file=file) def encode_special(flags): """Encode menu special flags.""" special_mask = 0 if "skip_this_mother_id" in flags and flags["skip_this_mother_id"]: special_mask |= 0x00000001 special_mask |= flags["skip_this_mother_id"] << 28 if "enter_act_as_exit" in flags and flags["enter_act_as_exit"]: special_mask |= 0x00000004 if "back_twice" in flags and flags["back_twice"]: special_mask |= 0x00000008 if "exit_act_as_enter" in flags and flags["exit_act_as_enter"]: special_mask |= 0x00000010 if "leave_background" in flags and flags["leave_background"]: special_mask |= 0x00000020 if "exit_calls_with_ff" in flags and flags["exit_calls_with_ff"]: special_mask |= 0x00000040 if "exit_leaves_menufile" in flags and flags["exit_leaves_menufile"]: special_mask |= 0x00000080 if "init_calls_with_0" in flags and flags["init_calls_with_0"]: special_mask |= 0x00000100 if "left_right_as_call" in flags and flags["left_right_as_call"]: special_mask |= 0x00000200 if "enter_only_calls" in flags and flags["enter_only_calls"]: special_mask |= 0x00000400 if "exit_only_calls" in flags and flags["exit_only_calls"]: special_mask |= 0x00000800 if "auto_press_enter" in flags and flags["auto_press_enter"]: special_mask |= 0x00001000 if "enter_leaves_menufile" in flags and flags["enter_leaves_menufile"]: special_mask |= 0x00002000 if "init_calls" in flags and flags["init_calls"]: special_mask |= 0x00004000 if "accept_incoming_request" in flags and flags["accept_incoming_request"]: special_mask |= 0x00008000 if "back_three_times" in flags and flags["back_three_times"]: special_mask |= 0x00010000 if "exit_disable" in flags and flags["exit_disable"]: special_mask |= 0x00020000 if "exit_load_pointer" in flags and flags["exit_load_pointer"]: special_mask |= 0x00040000 special_mask |= flags["exit_load_pointer"] << 24 if "exit_calls" in flags and flags["exit_calls"]: special_mask |= 0x00080000 if "init_calls_with_1" in flags and flags["init_calls_with_1"]: special_mask |= 0x00100000 if "exit_load_menu" in flags and flags["exit_load_menu"]: special_mask |= 0x00200000 if "only_bt_on" in flags and flags["only_bt_on"]: special_mask |= 0x00400000 if "only_datalog_enabled" in flags and flags["only_datalog_enabled"]: special_mask |= 0x00800000 return special_mask def convert_menu(info, out_file): """Convert to MENU format.""" item_size = 0x1D items = info["items"] data_size = item_size * len(items) item_pixels_x = info["item_pixels_x"] item_pixels_y = info["item_pixels_y"] basename = os.path.basename(os.path.splitext(out_file)[0]) with open(out_file, "w") as f: print(f"const UBYTE {basename}[] =", file=f) print("{", file=f) print_hex(" ", ">H", 0x0700, file=f) print_hex(" ", ">H", data_size, file=f) print_hex(" ", "B", item_size, file=f) print_hex(" ", "B", len(items), file=f) print_hex(" ", "B", item_pixels_x, file=f) print_hex(" ", "B", item_pixels_y, file=f) for i in items: print("", file=f) if i["icon_text"].strip(): print(f" // {i['icon_text']}", file=f) special_mask = 0 if "flags" in i: special_mask = encode_special(i["flags"]) print_hex( " ", ">LLBBBB", i["item_id"], special_mask, i["function_index"], i["function_parameter"], i["file_load_no"], i["next_menu"], wrap=4, file=f, ) print_hex( " ", f"{item_size - 13}s", i["icon_text"].encode("ascii"), file=f ) print_hex(" ", "B", i["icon_image_no"], file=f) print("};", file=f) p = argparse.ArgumentParser(description=__doc__) p.add_argument("info", help="input TOML file") p.add_argument("-o", "--output", metavar="FILE", help="output header file") options = p.parse_args() try: with open(options.info, "rb") as f: info = tomllib.load(f) if info["format"] == "menu": convert_menu(info, options.output) else: raise RuntimeError("Unknown format") except Exception as e: try: os.remove(options.output) except FileNotFoundError: pass print(e, file=sys.stderr) sys.exit(1)