Tech Support > Computer Hardware > Microprocessors > mixing C and assembly
mixing C and assembly
Posted by Paul Keinanen on April 24th, 2008


On Wed, 23 Apr 2008 08:26:59 -0400, Walter Banks
<walter@bytecraft.com> wrote:


While you might be able to write the startup code completely in C, but
does this works with different optimization switches, with different
compiler versions or even with different compilers ?

The ability to understand the generated assembly code will
significantly help, if you have to move into a different environment.

Paul


Posted by Mark Borgerson on April 24th, 2008


In article <480F499B.70EA80B4@bytecraft.com>, walter@bytecraft.com
says...

Imagecraft for the MSP430, IAR for the ARM, IAR for
the MSP430 and Codewarrior for M68K systems all seem
to use an assembly-language routine for initial setup.

In the case of the Atmel ARM chips, there's also some
memory chip selects and block remapping to do. Thankfully,
you generally don't have to touch that code unless,
as I have, you need a custom memory map to separate a
boot monitor from the user application.

Mark Borgerson



Posted by Mark Borgerson on April 24th, 2008


In article <f9b88f12-3621-441c-ad80-1db7328f7621
@b64g2000hsa.googlegroups.com>, compton75@hotmail.com says...
do is define the proper pointer values to access the
hardware registers. I've done it that way for UARTS
and USB devices on ARMS and M68Ks for years. You
can even set up the chip selects using their
memory-mapped control registers.

Mark Borgerson




Posted by Grant Edwards on April 24th, 2008


On 2008-04-24, Paul Keinanen <keinanen@sci.fi> wrote:

<lecture>

I don't think it's possible to emphasize that last point
enough.

Especially if you're developing for a small processor you're
never going to be very successful if you don't know how the
code generation differs for logically equivalent blocks of code.

For example, two ways to sum the bytes in a buffer:

unsigned char buf[128];
unsigned sum = 0;

unsigned i;
for (i=0; i<sizeof buf; ++i)
sum += buf[i];

unsigned char *p;
p = buf;
while (p < buf + (sizeof buf))
sum += *p++;

Those two blocks of code do the same thing. On some
target/toolchain combinations, they're both about the same
number of bytes/clocks. On some platforms I've used the first
was significantly smaller/faster. On others, the second was
significantly smaller/faster.

In the fist case, are you better off with an index that's an
"unsigned" or an "unsigned char" or an "int"? They can be
significatly different. Always pick something that will work
on as many platforms as possible, but if there are multiple
"correct" ways to do something, you might as well pick the one
that's going to generate the smallest/fastest code for the
target.

If you want to be more than an amateur doing trivial projects,
you've got to know your target's instruction set and know what
code the compiler is going to generate each time you write a
line of C.

I always set up my makefile so that the toolchain produces a
mixed C/assembly listing for each module. Anytime I'm not sure
what the compiler is going to do with a particular construct, I
look at the listing and see.

</lecture>

--
Grant Edwards grante Yow! I invented skydiving
at in 1989!
visi.com

Posted by Neil on April 24th, 2008


Walter Banks wrote:
main after setting up the C environment.

Posted by Paul Keinanen on April 24th, 2008


On Wed, 23 Apr 2008 20:56:28 -0500, Grant Edwards <grante@visi.com>
wrote:

The situation is not so problematic with C, but if you insist of using
C++ in some embedded project, you _really_ have to understand, what
resources a specific construct will require on your platform.

Paul


Posted by Chris H on April 24th, 2008


In message
<f9b88f12-3621-441c-ad80-1db7328f7621@b64g2000hsa.googlegroups.com>,
compton75@hotmail.com writes
--
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
\/\/\/\/\ Chris Hills Staffs England /\/\/\/\/
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/




Posted by David Brown on April 24th, 2008


Bill Leary wrote:
Why should the system do something reasonable if the programmer does
something unreasonable? In my gcc programs, main is tagged with a
"noreturn" attribute - attempting to return from main will produce at
least a warning message, possibly an error (I can't remember off-hand).
I'm sure Walter's compilers are able to enforce no return from main
equally well.

Posted by David Brown on April 24th, 2008


Paul Keinanen wrote:
If it is written correctly, then for the most part the answer is yes.
But when you are dealing with such low-level coding, you should be
prepared at least to re-check the code when changing tools.

Although technically written in C, start-up code often contains C
statements that are just thin wrappers around assembly code, using
macros, inlined functions, intrinsics, and the like. The definitions of
these will often need modified for different compilers, but the usage of
these macros and inlines is mostly independent of the compiler.

The ability to understand the generated assembly code is *always* a good
thing in embedded development. It is particularly relevant during such
critical startup code (as well as in any time-critical parts of the code).

By writing startup routines in C rather than assembly, however, you
reduce your requirements to an understanding of assembly, rather than
the knowledge to write good assembly for the target. IIRC, I first
started using C for startup code when targeting a PPC processor. Until
you get used to it, it is hard to write good PPC assembly. Rather than
trying to figure out which addressing modes made sense, which registers
are used for which functions in the ABI, and so on, it was much easier
to write it in C and let the compiler figure out the details. My task
was then reduced to checking the generated assembly (and the not
insignificant task of figuring out how to get the chip's hardware
configured!).


Posted by Walter Banks on April 24th, 2008




CBFalconer wrote:

Force of habit. Both are actually supported in our compilers.
register_sp existed before the ISO naming rules __register_sp
was alludes after and register_sp was kept so old code
wouldn't break with updated compilers.

Your point is well taken, I should have used the proper current syntax.

w..



Posted by Walter Banks on April 24th, 2008




Mark Borgerson wrote:

Mark,

Actually Byte Craft compilers are a little more complex. The compiler
/ linker keeps track of the things that architecturally need to be
initialized and generates code for this after reset. This usually initializes
the stack from either default, declared values in the application or
linker information. There are other architectural dependent things
in some processor families like clock options default ISA's, memory
map tables. These choices are selected with application pragma's.

The function called __STARTUP if it exists in the application gets
executed as soon as the processor is stable but before the variables
are initialized. __STARTUP is used to support user initialization and
is the way many embedded products trap a maintenance startup
mode. (Maintenance modes are user defined often ground a output
pin when processor is reset, jump or call maintenance software
in the application)

__STARTUP is written like any C function. When it is called no
variables have been initialized so if it goes into a maintenance mode
the variables have their values pre-reset.

After __STARTUP returns the remaining compiler generated
initialization is executed and then there is a jump to main(..).

The compiler/linker in the simplest case just ties the reset vector
to main (default stack) in the most complex goes through the
steps outlined above.

Regards,

--
Walter Banks
Byte Craft Limited
Tel. (519) 888-6911
http://www.bytecraft.com
walter@bytecraft.com






Posted by cbarn24050@aol.com on April 24th, 2008


On Apr 24, 12:43Â*am, David Brown
<david.br...@hesbynett.removethisbit.no> wrote:
No we're talking about startup code, do try to keep up.

Firstly you would be mad to try running C of these chips. Secondly you
would not put call returns on the hardware stack unless you have some
kind of death wish, you keep that space for the ISRs.


Posted by Bill Leary on April 24th, 2008


"David Brown" <david@westcontrol.removethisbit.com> wrote in message
news:48102f95$0$23838$8404b019@news.wineasy.se...
Because programmers WILL do something unreasonable and helping them to
discover what that is, beyond the system going bye-bye, is good engineering.
A bit of work here can potentially save a pile of work later on.

OK. So you DID do the "something reasonable," using a feature provided by
your development package.

- Bill


Posted by cbarn24050@aol.com on April 24th, 2008


On Apr 24, 12:13�am, CBFalconer <cbfalco...@yahoo.com> wrote:
Indeed you do but it's not usually more than 2 bytes, you cant get
much more optimal than that.


Posted by cbarn24050@aol.com on April 24th, 2008


On Apr 24, 8:18�am, David Brown <da...@westcontrol.removethisbit.com>
wrote:
Anything can upset a program, you need some kind of error code, also
needs to be in assembly, to make the system safe.

Posted by David Brown on April 24th, 2008


cbarn24050@aol.com wrote:
Walter writes compilers for a living, so his comments are based at least
partly on that - and I was commenting on your comments on his comments,
if you see what I mean.

But we are also talking about project-specific startup code.

When you are writing your own startup code, as is appropriate for some
projects, I can't really see why you would intentionally and knowingly
want to waste some ram, flash and run-time by specifically choosing a
call when a jump will do the job. If the source code is neater or
clearer, or faster to write, then that is obviously the decider. But
all other factors being equal, a jump will do the same thing.

In fact, if you are using a compiler that does some sort of
whole-program optimisation, and you write your startup code in C, you
can expect that you will get neither a call to main, nor a jump to main
- the entire main() function will be inlined within the startup code.

Firstly, you must then call me mad, along with all the customers for PIC
C compilers targeted at the smallest PICs, and ImageCraft's dedicated
AVR Tiny C compiler. I've only used a Tiny once, but I wrote that
program in C (using gcc - with a bit of arm-twisting to make it work
without RAM). I studied the generated assembly - it is unlikely that I
could have improved on it more than a few percent if I had hand-write
the assembly.

Secondly, the AVR Tiny has no ram beyond its 32 cpu registers. I find
it hard to imagine that you could write a program that needed more than
3 levels of calls (including interrupts) and have space in those
registers to make a software call stack. These are devices aimed at
small and simple programs - three levels of call stack is often sufficient.

Posted by Walter Banks on April 24th, 2008




cbarn24050@aol.com wrote:

A layer of a stack on a PIC with only 8 levels (In one case 2 levels)
has a big impact the available subroutine return stack.

w..




Posted by CBFalconer on April 24th, 2008


Walter Banks wrote:
But the 8 level pic, at least, simply overwrites the eldest stack
return address if overused. This puts you back to the condition
where you didn't stack anything, because the stack is used modulo
8. Of course the only way to stack that return is to call in the
first place. :-(

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.


** Posted from http://www.teranews.com **

Posted by Vladimir Vassilevsky on April 24th, 2008




David Brown wrote:

Even the good intelligent people sometimes fall into the belief of their
own hype :-)

Not too long ago I have burned up with the C startup code in C: it
called the intrinsic function. Initially, that function was inlined;
however after the new bugs in the CPU were discovered, that intrinsics
had to be implemented as a walkaround function in the C library. So the
startup called the library function before the library was intialized,
and it caused all sorts of weird problems. It was quite difficult to
find the cause.


Vladimir Vassilevsky
DSP and Mixed Signal Design Consultant
http://www.abvolt.com

Posted by steve on April 24th, 2008


On Apr 22, 4:50*am, Lax <Lax.Cla...@gmail.com> wrote:
The new Cortex M3 is advertised by ARM not to require any assembly
code, even boot up or ISR's.

http://www.arm.com/pdfs/Cortex_M3_DS.pdf
http://www.arm.com/pdfs/IntroToCortex-M3.pdf

"The Cortex-M3 processor includes many features that enable faster
software development, with developers not required to write any
assembler code or have a deep knowledge of the processor and its
register set."

"By handling the stack operations in hardware, the Cortex-M3 processor
removes the need to write assembler wrappers that are required to
perform stack manipulation for traditional C-based interrupt service
routines,
making application development significantly easier."

"Traditionally ISRs require an assembler wrapper to handle stack
manipulation before and after the main C-based routine is called and
during start-up boot code. The Cortex-M3 processor handles all stack
manipulation in hardware, removing the need for the assembler and
enabling the developer to program just in C and without having to
learn exactly how the processor and all of the register
banks operate."



Similar Posts