QUERY : ARM mode inline instructions in C Thumb mode file?

hello:

one small question regarding use of ARM inline assembly code in a C file that has been compiled for Thumb mode.

is it possible to use ARM assembly code from within a C file that has been compiled for Thumb and Thumb interworking?

how this is done is described on this page:

formatting link

i have a C file that has been compiled for Thumb mode. in it, i am using ARM inline assembly code. apparently, GCC issues no error message but forcibly converts the ARM code into Thumb code.

i think that GCC requires an entire file to be in either Thumb mode or ARM mode, but i am not sure. is that true?

the C code that is compiled into Thumb is:

void function_f( void ) { asm volatile ( ".align \n\t" ".arm \n\t" "mrs r0, cpsr \n\t" "msr cpsr_c, r0 \n\t" "pop { r0 }" ); }

the C code is compiled with: arm-elf-gcc -mlittle-endian -mcpu=arm7tdmi -march=armv4t \ -mthumb-interwork -mthumb -mno-tpcs-frame .....

here is the generated code:

00008194 : 8194: b580 push {r7, lr} 8196: af02 add r7, sp, #8 8198: 0000 lsls r0, r0, #0 819a: e10f b.n 83bc 819c: f000 e121 blx 4083e0 81a0: 0001 lsls r1, r0, #0 81a2: e8bd 46bd ldmia.w sp!, {r0, r2, r3, r4, r5, r7, r9, sl, lr} 81a6: b082 sub sp, #8 81a8: bc80 pop {r7} 81aa: bc01 pop {r0} 81ac: 4700 bx r0

in the inline assembly code, if i do not use ".arm", i get compiler errors.

is it possible to use ARM assembly code from within a C file that has been compiled for Thumb and Thumb interworking?

Aaron

Makefile

--------

CC = arm-elf-gcc TARGET_ARCH = -mlittle-endian -mcpu=arm7tdmi -march=armv4t TARGET_ARCH += -mthumb -mno-tpcs-frame -mthumb-interwork

all: test arm-elf-objdump -S -D test > test.lst

clean: rm -f test test.o function.o

C CODE

------

void function_f( void ) { asm volatile ( ".align \n\t" ".arm \n\t" "mrs r0, cpsr \n\t" "msr cpsr_c, r0 \n\t" "pop { r0 }" ); }

int main( void ) { function_f(); return 0; }

Reply to
adsouzp
Loading thread data ...

Try this:

void function_f( void ) { asm volatile ( ".code 32 \n\t" "mrs r0, cpsr \n\t" "msr cpsr_c, r0 \n\t" ".code 16 \n\t" "pop { r0 }" ); }

It generates:

function_f: .code 32 mrs r0, cpsr msr cpsr_c, r0 .code 16 pop { r0 } bx lr

You need to return to thumb mode after inserting the arm code or the assembles will get confused.

BTW, that "pop r0" will crash the program.

regards, Giovanni Di Sirio

--
ChibiOS/RT http://chibios.sourceforge.net
Reply to
gdisirio

That is still incorrect as it doesn't switch to ARM, nor switch back to Thumb... You can't do this unless you:

A. Switch to ARM mode for the whole function - many compilers can switch between ARM and Thumb on a per function basis. I don't know whether GCC supports this yet. The key is that the function_f symbol is marked as ARM, not Thumb, or it will be incorrectly called.

B. Use a Thumb function, like above, and explictly switch to ARM yourself, do your ARM sequence and BX lr to switch back if needed.

You should use a naked assembler function to stop the compiler from generating its own entry/exit sequences.

Btw why corrupt the stack using pop {r0}?

Wilco

Reply to
Wilco Dijkstra

Op Wed, 27 Aug 2008 11:44:59 +0200 schreef Wilco Dijkstra :

No attribute or pragma exists for this in the documentation.

--
Gemaakt met Opera's revolutionaire e-mailprogramma:  
http://www.opera.com/mail/
Reply to
Boudewijn Dijkstra

The above example was only meant to make it compile correctly, I didn't remove that "pop {r0}" either.

If you want to switch to arm mode and back to thumb mode then more instructions are required:

void function_f( void ) { asm volatile ( ".balign 4 \n\t" "mov r0, pc \n\t" "bx r0 \n\t" ".code 32 \n\t" "mrs r0, cpsr \n\t" "msr cpsr_c, r0 \n\t" "add r0, pc, #1 \n\t" "bx r0 \n\t" ".code 16 \n\t" ); }

which generates:

function_f: .balign 4 mov r0, pc // jumps into ARM mode bx r0 .code 32 mrs r0, cpsr // ARM code starting here msr cpsr_c, r0 add r0, pc, #1 // returns to thumb code bx r0 .code 16 bx lr // thumb code continues here

This one should be complete.

regards, Giovanni

--
ChibiOS/RT http://chibios.sourceforge.net
Reply to
gdisirio

switch

Yes, it is a big limit in the current implementation, they should really add new function attributes for that. That would make writing mixed code less painful.

--
ChibiOS/RT http://chibios.sourceforge.net
Reply to
gdisirio

hello:

a few apologies.

in this example, i was focussing on the ".arm" directive and thought that the assembler was not recognizing it. that's why the code is not semantically correct. the extra r0 is an error here.

i should have written that i have not included the code to move from ARM to Thumb mode and vice versa. that will require a bx r0, which i was planning to use. before using r0, i was planning to save r0 on the stack. that's why there is a pop r0.

i have also received a reply from Nick Clifton and will post his reply also.

i will also post my complete code that works, and i could not find it anywhere on the Internet. i wanted to simply add ARM mode inline assembly code into a Thumb mode C file for enabling/disabling interrupts without needing to call a function.

Aaron

Reply to
adsouzp

hello ffolks:

here is a reply from Nick Clifton, which is really illuminating. pls read it carefully. i will also post the complete working solution, if i can get permission from my boss.

-------- Original Message -------- Subject : Re: RESEND : QUERY : ARM inline code in Thumb file? Date : Wed 27 Aug 2008 10:37:23 +0100 From : Nick Clifton @ RedHat To : Aaron P. D'Souza

Hi Aaron,

Yes.

Highly unlikely. What is more likely is that you need to be careful about how you insert the ARM code.

There are two problems with the above code:

  1. You do not switch back to thumb mode at the end of the ARM code fragment. Gcc passes the contents of the asm() directory on to the assembler - it does not try to parse it and discover that you are sneakily switching into ARM mode...
  2. You have the .arm directive without a nearby label to hang it on. The .arm (and .thumb) directives need a label because the changes between instruction sets is encoded into attributes of the labels, thus allowing the linker and disassembler to know when instruction sets have changed.

ie. please try this:

void function_f( void ) { asm volatile ( ".align \n" "__f_into_arm:\n\t" ".arm \n\t" "mrs r0, cpsr \n\t" "msr cpsr_c, r0 \n\t" "pop { r0 } \n" "__f_from_arm:\n\t" ".thumb" ); }

Cheers Nick

Reply to
adsouzp

i tried the above code but it still did not work! here is the code generated.

00008194 : 8194: b580 push {r7, lr} 8196: af02 add r7, sp, #8

00008198 : 8198: 0000 lsls r0, r0, #0 819a: e10f b.n 83bc 819c: f000 e121 blx 4083e0 81a0: 0001 lsls r1, r0, #0 81a2: e8bd 46bd ldmia.w sp!, {r0, r2, r3, r4, r5, r7, r9, sl, lr}

000081a4 : 81a4: b08246bd strlth r4, [r2], sp 81a8: bc01bc80 stclt 12, cr11, [r1], {128} 81ac: 00004700 andeq r4, r0, r0, lsl #14

Aaron

Reply to
adsouzp

i tried the above code but it still did not work! here is the code generated.

00008194 : 8194: b580 push {r7, lr} 8196: af02 add r7, sp, #8

00008198 : 8198: 0000 lsls r0, r0, #0 819a: e10f b.n 83bc 819c: f000 e121 blx 4083e0 81a0: 0001 lsls r1, r0, #0 81a2: e8bd 46bd ldmia.w sp!, {r0, r2, r3, r4, r5, r7, r9, sl, lr}

000081a4 : 81a4: b08246bd strlth r4, [r2], sp 81a8: bc01bc80 stclt 12, cr11, [r1], {128} 81ac: 00004700 andeq r4, r0, r0, lsl #14

Aaron

Reply to
adsouzp

hello:

i should have added a few important points:

- i tried Nick Clifton's solution and it did not work

- i tried Giovanni's solution and it did not work

- the entire C code is compiled to Thumb mode

- function function_f() is compiled to Thumb mode

- GNUARM gcc4.1.1 is being used

- the code is being checked in disassembled code generated from the final .ELF executable file, disassembly being done using arm-elf-objdump.

i hope that this information is helpful.

Aaron

Reply to
adsouzp

Try this:

----------

#include

/* Change PSR - return old value */

uint32_t change_psr(uint32_t mask, uint32_t data) _attribute__((noinline));

uint32_t change_psr(uint32_t mask, uint32_t data) { uint32_t result;

asm volatile ( "adr r3,chg32\n\t" /* -> 32 bit code */ "bx r3\n\t" /* enter 32 bit mode */

".code 32\n" "chg32:\n\t" "mrs %[r],cpsr\n\t" /* get current PSR */ "bic r3,%[r],%[m]\n\t" /* clean up old PSR */ "orr r3,r3,%[d]\n\t" /* insert new bits */ "msr cpsr,r3\n\t" /* update current PSR */

"adr r3,chg16+1\n\t" /* -> 16 bit code */ "bx r3\n\t" /* return to 16 bit mode */

".code 16\n" "chg16:"

: [r] "=&l" (result) : [m] "r" (mask), [d] "r" (mask & data) : "r3"); return result; }

----------

HTH

--

Tauno Voipio
tauno voipio (at) iki fi
Reply to
Tauno Voipio

As far as I can see on the gcc website, gcc 4.4.0 supports changing compiler options on a function-by-function basis (either as pragmas for a block of code, or function attributes). However, this feature is currently only enabled on the x86 target (according to the docs). But I expect it will come soon on other targets.

Reply to
David Brown

That's because you need to first use .arm and then define the label. The label uses the current instruction set, and doesn't look at whatever comes afterwards. The disassembly is what I'd expect it to be.

Wilco

Reply to
Wilco Dijkstra

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.