From eb8aaccfaf3e80305c1d8652d050face4e594212 Mon Sep 17 00:00:00 2001 From: dwelch Date: Sun, 27 Mar 2016 13:00:14 -0400 Subject: [PATCH] blinker01 example for pi2 in svc mode --- boards/pi2/SVC/blinker01/Makefile | 71 +++++++++++++ boards/pi2/SVC/blinker01/README | 129 ++++++++++++++++++++++++ boards/pi2/SVC/blinker01/blinker01.bin | Bin 0 -> 272 bytes boards/pi2/SVC/blinker01/blinker01.c | 61 +++++++++++ boards/pi2/SVC/blinker01/blinker01.elf | Bin 0 -> 33792 bytes boards/pi2/SVC/blinker01/blinker01.hex | 19 ++++ boards/pi2/SVC/blinker01/blinker01.list | 113 +++++++++++++++++++++ boards/pi2/SVC/blinker01/blinker01.o | Bin 0 -> 1148 bytes boards/pi2/SVC/blinker01/memmap | 11 ++ boards/pi2/SVC/blinker01/vectors.o | Bin 0 -> 904 bytes boards/pi2/SVC/blinker01/vectors.s | 58 +++++++++++ 11 files changed, 462 insertions(+) create mode 100644 boards/pi2/SVC/blinker01/Makefile create mode 100644 boards/pi2/SVC/blinker01/README create mode 100755 boards/pi2/SVC/blinker01/blinker01.bin create mode 100644 boards/pi2/SVC/blinker01/blinker01.c create mode 100755 boards/pi2/SVC/blinker01/blinker01.elf create mode 100644 boards/pi2/SVC/blinker01/blinker01.hex create mode 100644 boards/pi2/SVC/blinker01/blinker01.list create mode 100644 boards/pi2/SVC/blinker01/blinker01.o create mode 100644 boards/pi2/SVC/blinker01/memmap create mode 100644 boards/pi2/SVC/blinker01/vectors.o create mode 100644 boards/pi2/SVC/blinker01/vectors.s diff --git a/boards/pi2/SVC/blinker01/Makefile b/boards/pi2/SVC/blinker01/Makefile new file mode 100644 index 0000000..f21abf9 --- /dev/null +++ b/boards/pi2/SVC/blinker01/Makefile @@ -0,0 +1,71 @@ + +ARMGNU ?= arm-none-eabi + +AOPS = --warn --fatal-warnings +COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding + + + +gcc : blinker01.hex blinker01.bin + +all : gcc clang + +clean : + rm -f *.o + rm -f *.bin + rm -f *.hex + rm -f *.elf + rm -f *.list + rm -f *.img + rm -f *.bc + rm -f *.clang.opt.s + +vectors.o : vectors.s + $(ARMGNU)-as vectors.s -o vectors.o + +blinker01.o : blinker01.c + $(ARMGNU)-gcc $(COPS) -c blinker01.c -o blinker01.o + +blinker01.elf : memmap vectors.o blinker01.o + $(ARMGNU)-ld vectors.o blinker01.o -T memmap -o blinker01.elf + $(ARMGNU)-objdump -D blinker01.elf > blinker01.list + +blinker01.bin : blinker01.elf + $(ARMGNU)-objcopy blinker01.elf -O binary blinker01.bin + +blinker01.hex : blinker01.elf + $(ARMGNU)-objcopy blinker01.elf -O ihex blinker01.hex + + + + + + +LOPS = -Wall -m32 -emit-llvm +LLCOPS = -march=arm -mcpu=arm1176jzf-s +LLCOPS0 = -march=arm +LLCOPS1 = -march=arm -mcpu=arm1176jzf-s +COPS = -Wall -O2 -nostdlib -nostartfiles -ffreestanding +#OOPS = -std-compile-opts +OOPS = -std-link-opts + +clang : blinker01.clang.hex blinker01.clang.bin + + +blinker01.clang.bc : blinker01.c + clang $(LOPS) -c blinker01.c -o blinker01.clang.bc + +blinker01.clang.opt.elf : memmap vectors.o blinker01.clang.bc + opt $(OOPS) blinker01.clang.bc -o blinker01.clang.opt.bc + llc $(LLCOPS) blinker01.clang.opt.bc -o blinker01.clang.opt.s + $(ARMGNU)-as blinker01.clang.opt.s -o blinker01.clang.opt.o + $(ARMGNU)-ld -o blinker01.clang.opt.elf -T memmap vectors.o blinker01.clang.opt.o + $(ARMGNU)-objdump -D blinker01.clang.opt.elf > blinker01.clang.opt.list + +blinker01.clang.hex : blinker01.clang.opt.elf + $(ARMGNU)-objcopy blinker01.clang.opt.elf blinker01.clang.hex -O ihex + +blinker01.clang.bin : blinker01.clang.opt.elf + $(ARMGNU)-objcopy blinker01.clang.opt.elf blinker01.clang.bin -O binary + + diff --git a/boards/pi2/SVC/blinker01/README b/boards/pi2/SVC/blinker01/README new file mode 100644 index 0000000..4f885ce --- /dev/null +++ b/boards/pi2/SVC/blinker01/README @@ -0,0 +1,129 @@ + +See the top level README for information on where to find documentation +for the raspberry pi and the ARM processor inside. Also find information +on how to load and run these programs. + +This example is for the pi2, see other directories for other flavors +of raspberry pi. This example switches back to SVC mode from HYP mode. + +This is an LED blinker example. + +The pi2 has two LEDs tied to gpios 35 and 47. + +Being the first example I will spend a little more time describing it. +I have some other ramblings on baremetal and the gnu tools so I will +try not to duplicate that. + +The primary use case for the raspberry pi is to run some version of +linux. The three main files to do that are bootloader.bin, start.elf +and kernel.img. The first two being GPU programs the last being ARM. +If you have no other files (dont have a config.txt) they (starg.elf +GPU code) copy the kernel.img file to 0x8000 in RAM, place some +code at address 0x0000 that is intended to prepare the ARM for booting +linux, then branch to 0x8000. We simply replace the kernel.img file +with our own program. I prefer to not mess with config.txt stuff +because it is not the primary use case, most pis out there do not use +this, so without is significantly better tested. Also over time the +config.txt things you can play with come and go and change name, some +of the popular ones are undocumented and so on. So I really dont want +to rely on that. Simply replace the kernel.img and otherwise be stock. + +vectors.s is the entry point for this program, even an application on +an operating system like linux has some assembly up front before +calling the main function. For this processor the minimum is to to +set up the stack pointer and call the main function. Because some +compilers add extra stuff if they see a main() funtion I use some +function name other than main() typically for embedded systems like this. +I have adopted the habit of using notmain() both to not be named main() +and to emphasize this is bare metal and not your average application. + +See my ramblings on .data and .bss, I dont need/use them so the +bootstrap (little bit of assembly before calling the first C function) +does not have to prepare those segments. I only need to setup the +stack and call the first C function in the project. + +I normally would set the stack pointer at the top of ram...Well that is +a lie, normaly one would do such a thing, but with code like this that +mostly ports across a number of boards, it becomes a pain keeping track +of who has how much ram. Instead for simple examples I set the stack +somewhere where it doesnt collide with the code, but also I dont have to +change every board. Because I am using the 0x8000 entry point I can +set the stack at 0x8000 and it will grow down toward 0x0000, and that +is more than enough for these projects. The way the ARM works it +subtracts then writes stuff so the first thing on the stack will really +be at 0x7FFC, you dont have to set it to 0x7FFC to avoid the code at +0x8000. + +The gpio pin is setup as an output to drive the LED. The blink rate +appears to be around a couple-three times a second, but may vary based +on your compiler and settings. This program does not attempt to use +any other peripherals (a timer) it relies on simply wasting time in a +loop then changing the state of the LED. If you were to use this line +of code: + +for(ra=0;ra<0x1000;ra++) continue; + +The optimizer will replace that with this one assignment: + +ra = 0x1000; + +And actually since that value isnt used again, it is dead code the +optimizer will likely remove that as well. + +One way to get around this is to make the loop variable volatile, this +tells the compiler every time you use it grab it from and save it back +to its home in ram. I prefer a different approach. I have a simple +dummy function in assembly language, it simply returns. + +.globl dummy +dummy: + bx lr + +The assembly is outside the visibility of the optimizer as would +anything basically not in the same file (llvm is a little different it +might "see" those other objects and optimize across them, gnu wont). + +So by having that external function and by passing the loop variable +to it. + +for(ra=0;ra<0x1000;ra++) dummy(ra); + +We force the compiler to actually implement this code and run that loop +that many times. Dont need to declare the variable volatile. If +uncomfortable with assembly langauge you could create a dummy function +in a separately compiled file + +void dummy ( void ) +{ +} + +Which produces the same code. + +00000000 : + 0: e12fff1e bx lr + +Some toolchains have the ability to see across objects when they +optimize so you still have to be careful. I prefer the assembly approach +to defeating the optimizer. + +So this program sets up the gpio to drive the LED. Uses the loop to kill +some time, changes state of the LED, repeat forever. The blink rate +will be the same for the same program. But compiler differences and +options can cause one build to be different from another in the blink +rate. It is not really deterministic, thus the desire to use timers in +the examples that follow. If you change the number the loop counts to +and re-build with the same tools, you should see a change in the +blink rate. + +Note the Broadcom documentation uses addresses 0x7Exxxxxx for the +peripherals. That is we assume the GPU's address space to access those +things. The ARM window into that is to date either at 0x20xxxxxx or +0x3Fxxxxxx depending on the specific broadcom chip for that board type +the pi2 uses 0x3Fxxxxxx so when you see 0x7Exxxxxx replace that +0x7E with 0x3F. + +I normally dont leave the compiled output in the repository, but you +may need it to compare with your results to see why for example mine +works and yours doesnt, so I will leave these here for this example. + + diff --git a/boards/pi2/SVC/blinker01/blinker01.bin b/boards/pi2/SVC/blinker01/blinker01.bin new file mode 100755 index 0000000000000000000000000000000000000000..d62be48263d314685c30827c651d972ed5471d28 GIT binary patch literal 272 zcmZ9HF^a-q6okhZSqm`%K}ZT?VX1Dj{pJW-2q`^*dH@RrOF?hoe-sZPh@F2PE@+Bf zu~^u`-}%*TEQXnR?)7;ylY^2_eb`$^->20ZR0MlRN1R0E zbBU@rfs#J=!d=l+Jw!7%*KVw46e~{Te`3Cdmg}42m|KcA$>YpX5 HPBp0;N^6JY literal 0 HcmV?d00001 diff --git a/boards/pi2/SVC/blinker01/blinker01.c b/boards/pi2/SVC/blinker01/blinker01.c new file mode 100644 index 0000000..e224680 --- /dev/null +++ b/boards/pi2/SVC/blinker01/blinker01.c @@ -0,0 +1,61 @@ + +//------------------------------------------------------------------------- +//------------------------------------------------------------------------- + +extern void PUT32 ( unsigned int, unsigned int ); +extern unsigned int GET32 ( unsigned int ); +extern void dummy ( unsigned int ); +extern unsigned int GETCPSR ( void ); + +#define GPFSEL3 0x3F20000C +#define GPFSEL4 0x3F200010 +#define GPSET1 0x3F200020 +#define GPCLR1 0x3F20002C + +//------------------------------------------------------------------------- +int notmain ( void ) +{ + unsigned int ra; + unsigned int rb; + + ra=GET32(GPFSEL4); + ra&=~(7<<21); + ra|=1<<21; + PUT32(GPFSEL4,ra); + + ra=GET32(GPFSEL3); + ra&=~(7<<15); + ra|=1<<15; + PUT32(GPFSEL3,ra); + + ra=GETCPSR(); + if((ra&0x1F)==0x1A) rb=0x20000; + else rb=0x10000; + + while(1) + { + PUT32(GPSET1,1<<(47-32)); + PUT32(GPCLR1,1<<(35-32)); + for(ra=0;ra7;2C6=Yc%p|M$lx7yVu7u#Lg9it#!ipoPthoD2-?>RfU2|>pW-v@8r@ArGZdA$A1KDso1*|IE&hAq9a zk@+ISTKu>yV%w4!mZa8uWt+sq{pbZlsesQH@Kzv%21bB|PDPgi2rbfw+ytOU1p zKhVAHP9>Pu_?^ZwQVHUE7i;A4s>$OrRsB(`b(#KQ?Riu0@6q`6mVT2*{U7vqIFdZK zOJ`DNpU(3~^>d}O=}b+pT7+ExyOvYwe5ex4Nv0_Sop?>16NN zwW%Y?6Yh{ZAmWxEQu{O1aFUmo1AsQ_SbH1@A9^5kIWQ z#QLKlCia&6&yKjP*LIo?KWek2MZ?~Pyj}C*hJ3%~(TLu^=|>eydQ7;qnIG10#1C^v z@7J&4sfK(=bMyX7UE6g3jIPmrO#cN<$9+>2G5J+pulOblbGetD@rtFgTa=!(l;?8` zGM6sgk<8s(VLn?L7<6Z(XI928-5feDsVVa;6uo>pSCCs}FJ1D?)@bU+q|7en^AEy< z|KWK+dyICI>Gi|uH1NkwUB@AvF_DFhL?4dY^hJrT(JO=Xc%Z&_R{M5u=`+2irsw@0 s6WVwBceZ(+>23V_Pqfe6&)nC1b8|mOXS2@6^PVq@q_h?dbB-Cm0K0?OSO5S3 literal 0 HcmV?d00001 diff --git a/boards/pi2/SVC/blinker01/blinker01.hex b/boards/pi2/SVC/blinker01/blinker01.hex new file mode 100644 index 0000000..fb9a10f --- /dev/null +++ b/boards/pi2/SVC/blinker01/blinker01.hex @@ -0,0 +1,19 @@ +:1080000000000FE11F1000E21A0051E30500001A02 +:108010001F00C0E3130080E300F06FE104008FE273 +:1080200000F32EE16E0060E102D9A0E30F00A0E1B1 +:10803000070000EBFEFFFFEA001080E51EFF2FE1C6 +:10804000000090E51EFF2FE11EFF2FE100000FE171 +:108050001EFF2FE170402DE9A0009FE5F7FFFFEB29 +:108060000E16C0E3021681E390009FE5F1FFFFEBDF +:108070008C009FE5F1FFFFEB0E19C0E3021981E3CD +:108080007C009FE5EBFFFFEBEFFFFFEB1F0000E243 +:108090001A0050E30258A0030158A0130219A0E3EC +:1080A00060009FE5E3FFFFEB0810A0E358009FE5A9 +:1080B000E0FFFFEB0040A0E30400A0E1014084E208 +:1080C000E0FFFFEB040055E1FAFFFF1A0219A0E3FD +:1080D00034009FE5D7FFFFEB0810A0E324009FE5E5 +:1080E000D4FFFFEB0040A0E30400A0E1014084E2E4 +:1080F000D4FFFFEB040055E1FAFFFF1AE6FFFFEAA9 +:108100001000203F0C00203F2000203F2C00203F8B +:040000030000800079 +:00000001FF diff --git a/boards/pi2/SVC/blinker01/blinker01.list b/boards/pi2/SVC/blinker01/blinker01.list new file mode 100644 index 0000000..690cd6b --- /dev/null +++ b/boards/pi2/SVC/blinker01/blinker01.list @@ -0,0 +1,113 @@ + +blinker01.elf: file format elf32-littlearm + + +Disassembly of section .text: + +00008000 <_start>: + 8000: e10f0000 mrs r0, CPSR + 8004: e200101f and r1, r0, #31 + 8008: e351001a cmp r1, #26 + 800c: 1a000005 bne 8028 + 8010: e3c0001f bic r0, r0, #31 + 8014: e3800013 orr r0, r0, #19 + 8018: e16ff000 msr SPSR_fsxc, r0 + 801c: e28f0004 add r0, pc, #4 + 8020: e12ef300 msr ELR_hyp, r0 + 8024: e160006e eret + +00008028 : + 8028: e3a0d902 mov sp, #32768 ; 0x8000 + 802c: e1a0000f mov r0, pc + 8030: eb000007 bl 8054 + +00008034 : + 8034: eafffffe b 8034 + +00008038 : + 8038: e5801000 str r1, [r0] + 803c: e12fff1e bx lr + +00008040 : + 8040: e5900000 ldr r0, [r0] + 8044: e12fff1e bx lr + +00008048 : + 8048: e12fff1e bx lr + +0000804c : + 804c: e10f0000 mrs r0, CPSR + 8050: e12fff1e bx lr + +00008054 : + 8054: e92d4070 push {r4, r5, r6, lr} + 8058: e59f00a0 ldr r0, [pc, #160] ; 8100 + 805c: ebfffff7 bl 8040 + 8060: e3c0160e bic r1, r0, #14680064 ; 0xe00000 + 8064: e3811602 orr r1, r1, #2097152 ; 0x200000 + 8068: e59f0090 ldr r0, [pc, #144] ; 8100 + 806c: ebfffff1 bl 8038 + 8070: e59f008c ldr r0, [pc, #140] ; 8104 + 8074: ebfffff1 bl 8040 + 8078: e3c0190e bic r1, r0, #229376 ; 0x38000 + 807c: e3811902 orr r1, r1, #32768 ; 0x8000 + 8080: e59f007c ldr r0, [pc, #124] ; 8104 + 8084: ebffffeb bl 8038 + 8088: ebffffef bl 804c + 808c: e200001f and r0, r0, #31 + 8090: e350001a cmp r0, #26 + 8094: 03a05802 moveq r5, #131072 ; 0x20000 + 8098: 13a05801 movne r5, #65536 ; 0x10000 + 809c: e3a01902 mov r1, #32768 ; 0x8000 + 80a0: e59f0060 ldr r0, [pc, #96] ; 8108 + 80a4: ebffffe3 bl 8038 + 80a8: e3a01008 mov r1, #8 + 80ac: e59f0058 ldr r0, [pc, #88] ; 810c + 80b0: ebffffe0 bl 8038 + 80b4: e3a04000 mov r4, #0 + 80b8: e1a00004 mov r0, r4 + 80bc: e2844001 add r4, r4, #1 + 80c0: ebffffe0 bl 8048 + 80c4: e1550004 cmp r5, r4 + 80c8: 1afffffa bne 80b8 + 80cc: e3a01902 mov r1, #32768 ; 0x8000 + 80d0: e59f0034 ldr r0, [pc, #52] ; 810c + 80d4: ebffffd7 bl 8038 + 80d8: e3a01008 mov r1, #8 + 80dc: e59f0024 ldr r0, [pc, #36] ; 8108 + 80e0: ebffffd4 bl 8038 + 80e4: e3a04000 mov r4, #0 + 80e8: e1a00004 mov r0, r4 + 80ec: e2844001 add r4, r4, #1 + 80f0: ebffffd4 bl 8048 + 80f4: e1550004 cmp r5, r4 + 80f8: 1afffffa bne 80e8 + 80fc: eaffffe6 b 809c + 8100: 3f200010 svccc 0x00200010 + 8104: 3f20000c svccc 0x0020000c + 8108: 3f200020 svccc 0x00200020 + 810c: 3f20002c svccc 0x0020002c + +Disassembly of section .ARM.attributes: + +00000000 <.ARM.attributes>: + 0: 00002b41 andeq r2, r0, r1, asr #22 + 4: 61656100 cmnvs r5, r0, lsl #2 + 8: 01006962 tsteq r0, r2, ror #18 + c: 00000021 andeq r0, r0, r1, lsr #32 + 10: 4d524105 ldfmie f4, [r2, #-20] ; 0xffffffec + 14: 00377620 eorseq r7, r7, r0, lsr #12 + 18: 01080a06 tsteq r8, r6, lsl #20 + 1c: 04120109 ldreq r0, [r2], #-265 ; 0xfffffef7 + 20: 01150114 tsteq r5, r4, lsl r1 + 24: 01180317 tsteq r8, r7, lsl r3 + 28: 0244011a subeq r0, r4, #-2147483642 ; 0x80000006 + +Disassembly of section .comment: + +00000000 <.comment>: + 0: 3a434347 bcc 10d0d24 + 4: 4e472820 cdpmi 8, 4, cr2, cr7, cr0, {1} + 8: 35202955 strcc r2, [r0, #-2389]! ; 0xfffff6ab + c: 302e332e eorcc r3, lr, lr, lsr #6 + ... diff --git a/boards/pi2/SVC/blinker01/blinker01.o b/boards/pi2/SVC/blinker01/blinker01.o new file mode 100644 index 0000000000000000000000000000000000000000..23aa29aa3bd989a3e8ef954fe63b65e239ca4e30 GIT binary patch literal 1148 zcma)6O=}ZT6g_Wd64P2Grdpd=bc9GF>KOZh=*E;-L%~X@4P?d_c58J_bki@BjhIeO>)$# z)%#|t_M~y$oVBK{GUjg6t=W#<>Ofp0n3`K!yu18xaRF&9BZfrII4>@Ui~6|8i-NeU z!5VD2p53D8_2c+Vp}y0#Jm-Z6t8IHWtkz%vYi-?iogQg4*s?vZ-)U`n&LCRokt4Zx z1HZ#$Qa|!k0G&Qb(_!FDQlR=Q@ec=Days{?4l)dMq%kU$j}&v%e@Su ram + .bss : { *(.bss*) } > ram +} diff --git a/boards/pi2/SVC/blinker01/vectors.o b/boards/pi2/SVC/blinker01/vectors.o new file mode 100644 index 0000000000000000000000000000000000000000..fb610716243d9aef337dfae48236b1dda0feca33 GIT binary patch literal 904 zcma)5O)mpc6g{t1UkR;<5MPxbA(A0TSXfkiE=cNY&1*C?w50PSy3(Z`KcPRx->@?p z8#WRfD|PNnUkr(GCik6l-@Wh7ySJUIm5o)U6f6|R@Z|wR5kC@}^=S+(XrzjJ$pjvI zu~m%$y-D0vJ5jFU^~`I)%_H6%uYdzDa$l*&QSm-%wHMkm66L4C+N1~E`W)!EC%o9r zvO23XIjA(!e>?M#IUtE}X^fO-CD3%w;Z2 z*7a}w+YWz?(5e@ewcaCFSIKsYbbkU%2X7V~$KzeU?W*TcNvL0UK^Ci(s>78w0 ziZ%Lq`317{vs`C+m-y*Alm3Myv!r|ZcL-!AyS@q*nVX=GCNoRX1n)c~?`xvTdC6<0 zjn;kpi(JzW!%ljFo09)mp7hI{b6Z2|^;m~&>vd4uS`x0%QZx&jCmehlLF9oXCU+5& TJ`yzPHC*qG2j{qFVe5SYMIK&C literal 0 HcmV?d00001 diff --git a/boards/pi2/SVC/blinker01/vectors.s b/boards/pi2/SVC/blinker01/vectors.s new file mode 100644 index 0000000..1e8e3e8 --- /dev/null +++ b/boards/pi2/SVC/blinker01/vectors.s @@ -0,0 +1,58 @@ + +;@ ------------------------------------------------------------------ +;@ ------------------------------------------------------------------ + + +.globl _start +_start: + ;@ b skip + mrs r0,cpsr + and r1,r0,#0x1F + cmp r1,#0x1A + bne skip + bic r0,r0,#0x1F + orr r0,r0,#0x13 + msr spsr_cxsf,r0 + add r0,pc,#4 + msr ELR_hyp,r0 ;@ .word 0xe12ef300 + eret ;@ .word 0xe160006e +skip: + mov sp,#0x8000 + mov r0,pc + bl notmain +hang: b hang + +.globl PUT32 +PUT32: + str r1,[r0] + bx lr + +.globl GET32 +GET32: + ldr r0,[r0] + bx lr + +.globl dummy +dummy: + bx lr + +.globl GETCPSR +GETCPSR: + mrs r0,cpsr + bx lr + +;@------------------------------------------------------------------------- +;@------------------------------------------------------------------------- + + +;@------------------------------------------------------------------------- +;@ +;@ Copyright (c) 2012 David Welch dwelch@dwelch.com +;@ +;@ 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. +;@ +;@-------------------------------------------------------------------------