views:

410

answers:

3

Banging my head today ;)

Over here I asked about translating an ASM file to C, and from the responses it looked like there was no reasonable way to do it. Fine. So one of the responses suggested I just make use of the functions as-is and be done with it. Sounds good.

But how?

Holy crap I've tried everything I can think of! I'm using a Microchip brand MCU (PIC18F4480) and a Microchip brand IDE (MPLAB IDE) and the ASM files are Microchip authored... so you'd think I'd find some way of making use of them! So far no luck at all.

I don't know anything about ASM (Assembly). I code in C++ and frankly that's not going to change. There has got to be a way to access the functions in the Microchip ASM files without rewriting all my code in a new language.

If anyone wants to take a look, the ASM files and application notes are here.

+5  A: 

I would expect you to need to somehow build an object (.o) file from the assembly code, and then link your C++ file with that object file. You are likely going to need to declare those functions as extern "C" to call them from C++.

Not sure about the details here, there might be issues with calling conventions between C++ and assembly, especially for a microcontroller such as the PIC.

Are you really using a full C++ feature set, i.e. objects, virtual functions, overloading, and so on, for a microcontroller? That's ... not bad, but I thought most PIC development was still in plain C (and assembly, of course).

unwind
careless use of pluses. It is just c
Steven
+5  A: 

Looking at PIDInt.asm, it should be fairly straight-forward to call the functions from C/C++. First, you declare extern variables for everything listed in VARIABLE DEFINITIONS:

extern unsigned char error0, error1; /* etc */

Then, you declare extern functions for all the things that have a "Function:" comment, taking no arguments and returning no result:

extern void Proportional(); // extern "C" for C++

To call one of them, you fill the input variables, call the function, and read the output variables.

Martin v. Löwis
For C++, he'll also need `extern "C"` on function declarations.
Pavel Minaev
@Pavel: thanks, added.
Martin v. Löwis
And as stated in the other answer link the obj files together.
simon
nice catch. cheers for your attn to detail Pavel. thanks for your help people.
Steven
I haven't really looked to see if it applies (just woke up) but what if the function does take an argument and returns a result? Can extern not do that?
Steven
Wow - that's a pretty bad interface that Microchip came up with for this (maybe the PIC doesn't have a stack? I understand PIC's can be pretty low-resource chips). Unless the chip really doesn't have the resources, I'd wrap the stuff that Martin v. Löwis described in a C function of its own and have the wrapper use proper parameters so there's only one small bit of code dealing with the globals.
Michael Burr
@Steven - whether the function takes arguments and/or returns a result all depends on how it's written. C compilers have a set of rules (sometimes known as a 'calling convention' or an 'ABI') about how they pass parameters and obtain return values. As long as an assembly routine is written with these rules in mind, they can be called just like any other C function. However, there's nothing that requires an assembly routine to be written this way.
Michael Burr
@Michael It looks like the PIC assembly code example here was NOT written to be accessed from 'C' specifically, and does not make any attempt to deal with the 'C' stack frame. It's also worth noting different 'C' compilers may setup the call stack frames differently too.
simon
+2  A: 

The usual method is to use a build environment and tools that has an assembler as well as a C/C++ compiler. Compilers (and assemblers) take source files and produce an intermediate object file format. On Microsoft Windows, with Microsoft Tools, these would be .obj files.

A linker is then used to link the .obj files - which were potentially produced by a wide variety of languages - fortran, cobol, c++, c, objective-c, assembly etc, into a single application binary.

Object files are basically a collection of exported symbols, labeling little chunks of bytes, and unresolved symbols, labeling chunks of bytes that need to be patched during linking to point to something in a different object file.

Now, the usual problem - on the platforms Ive experienced which are not close to yours, are, c++ compilers are not made with any kind of cross language binary compatibility in mind. C compilers on the other hand, are. So c++ compilers make highly mangled symbols that incorporate all sorts of information about parameter and return types into the symbol names. This mechanism is what makes operator overloading possible.

Anyway, the crux is,

  • You need to build the asm files. If you dont have an assembler, you're outa luck. And it probably needs to come from the same vendor as your c/c++ toolset
  • You need to tell your c++ program the names of the external symbols. In a c compatible way. This is what header files are typically used for.

So create a header file to hold function declarations for the pid code and place Something like this in them:

extern "C" void PidMain(void);

Now, #include that header file in your c++ program, and ensure that the object files produced by the assembler are included in your environments link step and you should be golden.

There might be some kind of calling convention involved. Some environments have different standards as to the order things are pushed onto the stack, and/or who is responsible for stack cleanup. Most ms windows toolsets support at least a __pascal and __cdecl calling convention, so the declration would look like

extern "C" void __pascal PidMain(void);

I have no specific knowledge if there are multiple calling conventions in your specific environment. So I don't know how helpful this has been. Good luck I guess.

Chris Becke