a
a
aa
Phone: (800) ANALOG-D, FAX: (781) 461-3010, EMAIL: dsp.support@analog.com, FTP: ftp.analog.com, WEB: www.analog.com/dsp
Engineer To Engineer Note EE-128
Technical Notes on using Analog Devices’ DSP components and development tools
compiler where it modifies the name of a
DSP in C++ : Calling Assembly
Class Member Functions From
C++
member function to include information about
its input and output parameters. There are a
number of reasons why this happens but the
primary reason is so the compiler can
differentiate between different member
Submitted by DL 12/2000
When using a high-level language (i.e. C or
C++) to develop DSP code, it is often desirable
to hand-code time-critical sections in assembly
code to achieve optimal performance. These
assembly routines, however, must be callable
from that higher-level language. When
developing with C, this is a straightforward
process (see the VisualDSP++ C Compiler
Guide, page 2-58), but in C++, where we now
have to deal with things like inheritance and
name-mangling, it becomes much more complex
from an assembly standpoint.
This application note presents a basic scheme to
preserve the simplicity of performing standard C
calls from a C++ environment.
The material requires a basic understanding of
the C++ language and an object-oriented
programming environment. For some good
resources, see the References Section at the end
of this application note.
How is the class information passed to
class functions?
There are two major challenges that one meets
when trying to write C++ callable assembly
code, or more specifically, when one tries to
write a member function in assembly.
The first problem is name mangling. Name
functions with the same name. Member
functions of a class may have the same name as
long as the parameters they take are different.
For example, in the class listing below, there are
two constructors with the same name. The
compiler will append a text string to the labels
of each constructor to represent the parameters
they take.
class cDelay {
public:
float * Buffer;
float * Ptr;
int Length;
float Input, Output;
Delay( float input );
Delay( );
}
A function called Delay from the class cDelay,
which takes a single float as a parameter and
returns a single float, will receive the following
label from the compiler:
_Delay__9cDelay_STq2dmFv
Hence the mangled names... The problem here
is that unless you have some software to mangle
your names for you, it is extremely tedious to
manually determine what a mangled name is
going to be based on its parameters. If you wish
to interface a function to assembly, you must
know what this mangled name will be. As we
will see further on, there is a nice way to bypass
this whole process.
mangling is an operation performed by the C++
Copyright 2001, Analog Devices, Inc. All rights reserved. Analog Devices assumes no responsibility for customer product design or the use or application of customers’ prod ucts or
for any infringements of patents or rights of others which may result from Analog Devices assistance. All trademarks and logos are property of their respective holders. Information
furnished by Analog Devices Applications and Development Tools Engineers is believed to be accurate and reliable, however no responsibility is as sumed by Analog Devices
regarding the technical accuracy of the content provided in all Analog Devices’ Engineer-to-Engineer Notes.
Now that we understand that name mangling is
something we want to avoid, if possible, let’s
address the second issue : how do we access the
information in an instance of a class from a
member function of that class. When a
function is called in C++, the first argument
passed is the this pointer, or rather, the
pointer to the top of the class structure. This
passage is transparent to the programmer
(unless, of course, the programmer is trying to
program an assembly language function, which
we’ll get to soon).
In order to review how function parameters are
passed, we will first start with a C example.
When a C function is called, the parameters are
passed as follows:
1st parameter - R4 register
2nd parameter - R8 register
3rd parameter - R12 register
4th parameter - 1st location of stack
5th parameter - 2nd location of stack
…
For example, say we have a C function called
add whose prototype might look something like
this :
float add( float x, float y);
When we call Add, the x parameter is placed in
the R4 register and the y parameter is placed in
the R8 register.
From a C++ member function, the parameter
passing is slightly different. Instead of the first
parameter being passed in the R4 register, the
this pointer is passed in the R4 register and any
other parameters are passed in the same fashion
as is true for C, starting in the R8 register :
this (ptr to instance) R4 register
1st parameter - R8 register
2nd parameter - R12 register
3rd parameter - 1st location of stack
4th parameter - 2nd location of stack
…
Let’s start with the following class as an
example. In this example, the Add function will
add a real and an imaginary component to its
own Real and Imag variables. It will then return
the real sum.
Cplx_float.h
Class Complex_Float {
private :
float Real, Imag;
public :
Complex_Float(); // constructors
Complex_Float( float real,
float imag );
float Add( float real,
float imag );
};
If we declared a new instance of a
Complex_Float and called the Add function, the
parameters would be passed as follows :
R4 - pointer to the current instance of
Complex_Float
R8 - real parameter
R12 - imag parameter
For both C and C++, the return argument of
a function is always placed in the R0 register.
Now suppose we wanted to hand-code the
complex Add routine that we just defined in
assembly code.
Add_.asm
#include <asm_sprt.h>
.segment /pm seg_pmco;
// function name is arbitrary
_AsmAdd:
.global _AsmAdd;
// r4 contains a pointer to the
// instance
i4 = r4;
EE-128 Page 2
Technical Notes on using Analog Devices’ DSP components and development tools
Phone: (800) ANALOG-D, FAX: (781)461-3010, EMAIL: dsp.support@analog.com, FTP: ftp.analog.com, WEB: www.analog.com/dsp