Call C functions from assembly language project

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Call C functions from assembly language project

2,910 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cwpjr on Fri Feb 22 15:06:22 MST 2013
Originally Posted by [B]TheFallGuy[/B]                     [IMG]http://knowledgebase.nxp.com/images/buttons/viewpost.gif[/IMG]                
                 [I]Thats irrelevant. You can call C functions from assembly language.

[/I]
                                 What if I don't use, or know C?

All the help I can find is about inline assembly.

Are there examples to learn from?

How would the smallest c call/ included lib overheard configuration be integrated into an existing assembly language project?

Thanks, Clyde
0 Kudos
9 Replies

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sun Feb 24 15:48:54 MST 2013
[FONT=Tahoma][SIZE=1]
Quote: TheFallGuy
There is no standard comment character, it depends on the architecture. See
http://en.wikipedia.org/wiki/GNU_Assembler

[/SIZE][/FONT]
Quote: TheFallGuy
[FONT=Tahoma][SIZE=1]

To quote from the above page:[/SIZE][/FONT] [FONT=Tahoma][SIZE=1]

Single-Line comments:[/SIZE][/FONT] [FONT=Tahoma][SIZE=1]
Single line comments have a few different formats varying on which architecture is being assembled for.
Hash symbols are used for the platforms: i386, x86-64, i960, 68HC11, 68HC12, VAX, V850, m32r, and M880x0.
Semicolons are used on: AMD 29K family, ARC, H8/300 family, HPPA,PDP-11, picoJava, Motorola, and PowerPC.
The at sign is used on the ARM platform.
A vertical bar is used to signify comments when assembling on 680x0.[/SIZE][/FONT]

[FONT=Tahoma][SIZE=1]

I am not talking about the GNU implementation specifically [where the ARM comment details are set for
compatibility with ARM's assembler!], but historically.
E.g. DEC VAX 11, Motorola 6800[0], Intel from Z80 through current 32/64 bit with MASM, 8051 and variants, PIC, AVR, etc. and others
that I have forgotten about or died into obscurity.

As far as my 40 years experience goes, everybody used a semi-colon until ARM. For in-line comment to eol.
I am not considering full line 'block'  comments (ARM uses a hash/pound/number sign -- name varies with
geographic location.) I have not changed this but never use it.

Regards, Mike

[/SIZE][/FONT]
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by TheFallGuy on Sun Feb 24 15:32:55 MST 2013

Quote: MikeSimmonds
[FONT=Tahoma][SIZE=1]As promised, my number formatting code.


I object very strongly to the way that ARM (the company) abribtrarily overturned 50+ (well at least 40) years of
tradition and changed the comment character from ';' to '@' (ugly in quantity). They then suggest the we use
the hijacked ';' as a statement seperator. Is this an attempt to make assember resemble "C" -- a forlorn attempt!


[/SIZE][/FONT]



There is no standard comment character, it depends on the architecture. See
http://en.wikipedia.org/wiki/GNU_Assembler

To quote from the above page:

Single-Line comments:
Single line comments have a few different formats varying on which architecture is being assembled for.
Hash symbols are used for the platforms: i386, x86-64, i960, 68HC11, 68HC12, VAX, V850, m32r, and M880x0.
Semicolons are used on: AMD 29K family, ARC, H8/300 family, HPPA,PDP-11, picoJava, Motorola, and PowerPC.
The at sign is used on the ARM platform.
A vertical bar is used to signify comments when assembling on 680x0.
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sun Feb 24 14:48:17 MST 2013
[FONT=Tahoma][SIZE=1]As promised, my number formatting code.

Typically, allocate a small buffer (e.g. 128 bytes -- you can use the stack) and call a series of
strtext, strhex, strdec etc. to build up yor report string.

[B]Don't forget[/B] to add a final null byte after the last format chunk.

The send this string to 'puts'

[B]NOTE[/B] in the puts source code, change the uart0 equate to match the register base address for the
uart you choose to use as set out in the user manual for the device (LPC part) you are using.

The full text is too long to post, get the code from the zip:(
[Edit]
[B]This is important too![/B]

I object very strongly to the way that ARM (the company) abribtrarily overturned 50+ (well at least 40) years of
tradition and changed the comment character from ';' to '@' (ugly in quantity). They then suggest the we use
the hijacked ';' as a statement seperator. Is this an attempt to make assember resemble "C" -- a forlorn attempt!

In any case, I am not prepared to put up with this, so I patch the 'arm-non-eabi-as.exe' (just have to swap two
bytes -- as long as they are the correct two) to get back the ';' as the comment character.

Before attempting to assemble my sources, do a global replace of ';' with '@'.
If the editor cannot do this in one operation, get a better editor!

Also change 'thumbFunc' (in amacs.inc) to 'thumb_func'. I have a serious down on underscores in names etc.

I should appologise for the inconvenience, but I place the original blame on ARM, so I don't.
[/Edit]

Cheers, Mike
[/SIZE][/FONT]
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sun Feb 24 14:28:27 MST 2013
[FONT=Tahoma][SIZE=1]This is an aside -- Code in next post; But "read this first"
When I re-examined my code in more detail, I see that a lot of it will not compile on an M0 part.

As you are not employing me, I am not going to do anything about that. :p
There are standard work arounds that you can work out for yourself for the lack of the "it" instruction
and the "cbz/cbnz" etc.
More difficult to spot are the M0's limitation on the variations of the "mov" instruction etc.

The biggest problem is the lack of any way to divide by 10. You will have to make (or google/steal) a
subroutine for this yourself.

The M3 is much easier to work with when you are coding assmbler directly (in "C", someone else has
done the work). See poster image attached.

Are you 'wedded' to an M0 part? Can you upgrade to an M3 part with compatible flash/ram/peripherals.
If you are the 'hobby' or investigation/evaluation stage, I stongly urge a move to M3.

If this an actual live project where every penny is critical, you can afford to hire (as in pay) someone to
shoe horn into an M0 design.

If you are hobby, I consider it my public duty to [I]buy[/I] you a LPCXpresso 1769 eval board to wean you off the M0's
(But I am not going to:rolleyes:).

Having said all this, the next post is of my [I]Cortex-M3[/I] code snippets.

[Edit]
With Cortex M3, you also get the use of the high registers (R8 to R12). Sometimes this costs more in code space,
but you do have the option.
[/Edit]





[/SIZE][/FONT]
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cwpjr on Sat Feb 23 14:35:15 MST 2013
Not enough Beer charity events here in the U.S.:confused:

To Answer your Question Yes this was helpful, and I knew of the  background info included so I fully understand your input.

When I get my serial hardware up I will probably ask to see the code you say you can share. Until then Thanks so much again!

:)

Cheers, Clyde
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Sat Feb 23 09:06:58 MST 2013
[FONT=Tahoma][SIZE=1]As I said, I don't use "C" libraries, rather my C is sort of top level messsge handling for most of our systems.
The 'ease' and algorithm clarity overweighs the asm compactness (at least so my boss says).

Also my MPU is a Cortex M3, so I apologise if I give examples that don't build on your M0 system.

I have a few simple funcs, strdec, strhex, strtxt that I use to print numbers (as hex, as decimal) or copy
bits of text to a buffer. Sort of do it yourself printf sections without the printf itself.

I than have a (Part Specific) puts func that copys a null terminated string to a uart. [Busy waiting, no
interrupts etc.]

I am assuming you want to display debug/status messages over a serial comms link and have a uart
(+level converter) on your PCB. [If not some sort of specifics (also which MPU) might be useful.]
And that you can setup that uart.

I'm happy to give these to you (and anyone else), but it will have to wait till I wake up on Sunday ...
I'm off to a Beer Festival (Get Drunk For Charity!) so expect the worst:D for my hangover.

Mike.


[/SIZE][/FONT]
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cwpjr on Fri Feb 22 21:49:10 MST 2013
To Answer your Question Yes this was helpful, and I knew of the background info included so I fully understand your input. THANKS! You can ignore the rest of this.... :>)

I call my asm.s entry point from main.c and can return to the point in main.c after this call, via the mov pc, lr in my asm.s code.

My comments on c libs are that in some systems there are larger and smaller libs say for the stdio.h functions....

I would hope to simply make c calls to void input/output c routines, passing all my parameters in the 1rst 4 registers, to avoid the call stack crap. If this is undo-able please let me know!:eek:

Alternately I could probably fudge it (again tell me if I'm being foolish) by using the lr return to main.c in a loop that:
1) calls my asm.s entry function, then enters a begin until loop consisting of the below steps.
2) when I need say c i/o I would save the lr value that goes back to main.c and set lr to return to this point in asm.s
3) at this point in main.c I would save the current lr (IF NEED BE) then continue into a case statement would call the functions I wrote and want to use, doing what ever mangling to the c call stack of my register'd arguments, if need be.
4) use the lr value set in asm.s to return to the asm.s point before the lr to main.c

If this is unclear or undo-able or been solved a clearer way let me know please!

Good info to mull:cool: and I see that your examples may be simpler, if they don't hog my ARM M0 limited ram to bad...
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Fri Feb 22 20:16:02 MST 2013
[FONT=Comic Sans MS][SIZE=1]Actually, I think the point of asm calling "C" functions was so that you could call
things like printf.

These [I]are[/I] library functions so you would have to link against redLib/newLib and live with the
size of the library in your project.

And you wouldn't disable library(ies), startup files.

I'm a bit unsure about the following, but ...

The library startup code will probably take care of data/bss initialisation, but will probably
insist on starting at 'main' ( a "C" function)

I suggest that 'main' simply calls your top-level asm code (which never returns to main)
Something like
int main(void)
{
   MyAsmTopLevelStartPoint();
}
As I say I don't do things like that, so it may not be exactly true.
I'm sure that others will correct me (and tell me off).

The bits about the ABI and calling "C" from assembler are true anyway.

Mike

[/SIZE][/FONT]
0 Kudos

2,089 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by MikeSimmonds on Fri Feb 22 19:52:12 MST 2013
[FONT=Trebuchet MS][SIZE=1]Hi Clyde. Some hints, but not the full story (as that would take too long).

First you [B]must[/B] read up on the ABI (Application Binary Interface). Search the ARM site.
But in a nutshell, a function is called with up to 4 parameters in registers.

This is true for asm calls "C" and also "C" calls asm.

E.g. Func(a,b,c,d) will be called with r0=a, r1=b, r2=c, r3=d.

The 'type' can be anything e.g. ptr to "C" string (null terminated) a number promoted to long
taking sign into account if reqd.

This is the simple case, obviously you can have more than 4 arguements, but I won't cover that here.

The return value (if any) goes into r0.

Any function either "C" or asm must preserve r4-r11 (and lr/sp/pc of course -- I take it you know about the lr
and bx lr for single depth calls and pushing lr/popping pc when you function calls nested functions)

It can do whatever it likes with r0-r3 and also r12; bearing in mind the parameters/return rules stated above.
If you need more than these, you must save and restore what you use.

A silly example (better done in asm anyway)
The "C"

int SillyFunc(char *buffer, char startValue,  long length)
{
   long tot = 0; 
  while (length--)
  {
         *buffer++ = startValue;
         tot = tot + startValue;
         startValue += 11;
   }
   return(tot);
}
the asm calling it

...
   ldr   r0, =myBuffer
   movs r1, 0x42
  ldr  r2, =1492
  bl SillyFunc
  ldr   r1, =SomeVar
 str   r0, [r1]
...
I personally [I]never[/I] use any "C" library functions and (if I were building via the IDE) select options for the "C" build
to disable all libraries (newLib, redLib, ...)

Actually I build via an external make, the C flags from m makefile are:
CCFLAGS    += -mcpu=cortex-m3 -mthumb -funsigned-char -g3 $(OPTIM)
CCFLAGS    += -nostartfiles -nodefaultlibs -nostdinc
CCFLAGS    += -Wall -Wno-parentheses -fno-builtin
CCFLAGS    += -fmessage-length=0
CCFLAGS    += -I$(INCL)
CCFLAGS    += -save-temps -fverbose-asm
You want to set options to mimic this (esp the second line)
The last line causes the compilers asm output from the c file to be kept (as cfile.s, so DONT have a "C" and asm
with the same basename!)
You can look at this in an editor if you want to see how C translates to asm.

Going the other way is just as easy,

a "C" call:      var = AsmFunc(buff, startVal, byteCount);

would call your asm with r0 to r2 as described above; you would set r0 to whatever your return value is before
the bx lr to return to the next "C" statement.

One last point:
Unless you do things to change the game, "C" will put code into a .text section. initialised data to .data
and uninitialised data into .bss.

Your startup code must find the beginning and end of these and where the rom copy of the data lives and
copy data/clear bss to zero.

There are many examples of ways to do this on the forum/code red wiki, and in almost all samples (this is setup
so it doesn't really matter what sample it is).

There's also rodata* which you dont have to bother with as it stays in the flash (read only data).

Did you know this already, or is this helpful?

Mike

[/SIZE][/FONT]
0 Kudos