GNUH8 mixed C and assembly

I am working with HEW4 and GNUH8 5.03 targeted at the H8/3664 and H8/3687 and I can write C programs with only moderate difficulty. I seem to have no problem writing code in .src files and calling it from C. What I seem unable to find out is how to write functions in assembler and then either access the arguments that are passed in, or how to return values to the calling function.

Also, how can I know what registers it is safe to use in my assembler functions?

At the moment, all I want to do is to give a function an integer and possibly return an integer. Am I going to have to learn the arcane ways of the inline assembly stuff? An example project would be good.

FWIW, the exact need just now is a soft time delay. These are short delays of a few tens of microseconds I appreciate these are less than ideal in many ways. I can write one in C - as appears in any number of example projects. However, I am nervous about how the optimizer might treat my code. In other systems, it is not uncommon for the optimization to completely remove apparently inactive code fragments. I am running with -Os normally and tests seem to show that GCC will not remove lines like this:

int temp = 1000; while(temp--);

under any optimization but that the generated code (and thus the delay) may change between no optimization and any level of optimization. I do understand that that is exactly why soft delays are dodgy. Thus I figure that, if I must have a soft delay, it should really be in assembler so that I know what code is generated. Would I be better staying in C? Can I ever guarantee that code will not be removed if I change the optimization level?

Pete Harrison

Reply to
Peter Harrison
Loading thread data ...

Soft delays are never fixed if they can be interupted, if you need a fixed delay use a timer. There are different ways of including assembly into your programs. Inline lines or blocks of assy instructions, C functions who's code is written in assembler or pure assy routines called directly. You need to look at the compiler docs for the syntax. You can usually get to a C value from assember by putting an underbar in front of the variable name but the H8 instructions mostly work only with registers so this method is very limited. The other way is to use the frame pointer to acces the stack directly but this requires you to fully understand how your compiler works. The H8 also has complex assy intructions best avoided if humanly possible.

Reply to
cbarn24050

Interesting version numbers ?

Where did you get this package from?

You should have checked the documentation for the compiler.

If it is a genuine GCC/GNU compiler then you should find the link in my sig useful, also the following link:-

formatting link

Personally you have timers on chip, use them and interrupts to get your delays right. Your method only works if ABSOLUTELY nothing else is going to happen and the whole programme is linear.

--
Paul Carpenter          | paul@pcserviceselectronics.co.uk
    PC Services
              GNU H8 & mailing list info
             For those web sites you hate
Reply to
Paul Carpenter

.....

....

Considering the number of registers and sizes upto 32 bits not as limited as quite a few processors.

GCC/GNU uses R0 to R2 (for 16bit args) or ER0 to ER2 (32bit args) for the first THREE parameters, the rest are passed on the stack.

Actually quite easy to use, some of the modes on some instructions are limiting but a lot can be done in straight assembly. Most users create their own startup code in assembly.

-- Paul Carpenter | snipped-for-privacy@pcserviceselectronics.co.uk PC Services GNU H8 & mailing list info For those web sites you hate

Reply to
Paul Carpenter

KPIT cummins

formatting link
I should perhaps have noted that the version numbers were KPIT ones. The compiler is GCC 4 based.

I did try...

but, of all the pages I chased about on today, I missed that one :)

Thanks - that all makes more sense now.

I understand that. For many tasks, I do indeed use the timers. For delays of a few milliseconds or more, I would use a 1kHz timer tick I have. The steppers I am driving have pretty robust timing up to about

5000 interrupts per second. This is just for initialing a graphical LCD so all I really want is a minimum delay for a set-up time - typically a few tens of microseconds - and a simple busy-wait is fine.

Pete Harrison

Reply to
Peter Harrison

For something that non-critical, and where the only real concern is minimum time elapsed, I tend to use macros encapsulating a system-speed-dependent constant which I can play with.

Steve

formatting link

Reply to
Steve at fivetrees

That's definitely the wrong way to do it.

GCC's inline parameter syntax will cause the compiler to emit code to load the input value(s) into register(s) for you if you want it to. GCC's inline parameter syntax is very powerful and doesn't require that you know much of anything about how the compiler works. Just tell it what register(s) you want the input values in, what registers you're clobbering, and where to get the output values.

Yup, that's another wrong way to do it. The right way is to use the extended asm feature. Quoting from the gcc info page:

Assembler Instructions with C Expression Operands ================================================= In an assembler instruction using asm', you can specify the operands of the instruction using C expressions. This means you need not guess which registers or memory locations will contain the data you want to use. You must specify an assembler instruction template much like what appears in a machine description, plus an operand constraint string for each operand. For example, here is how to use the 68881's fsinx' instruction: asm ("fsinx %1,%0" : "=f" (result) : "f" (angle));

Here angle' is the C expression for the input operand while result' is that of the output operand. Each has "f"' as its operand constraint, saying that a floating point register is required. The =' in =f' indicates that the operand is an output; all output operands' constraints must use ='. The constraints use the same language used in the machine description (*note Constraints::.).

Really? I found H8 assembly to be quite orthogonal, striagh-forward, and very nice to work with.

--
Grant Edwards                   grante             Yow!  My DIGITAL WATCH
                                  at               has an automatic SNOOZE
                               visi.com            FEATURE!!
Reply to
Grant Edwards

You got already many good answers, but nobody seems to have told this yet:

For an assembly language subroutine, create a skeleton function in C and compile it with the GCC -S switch, so you get the assembler version of the function.

If you'd prefer an assembler listing instead, use

gcc -c -Wa,-ahlms=myfile.lst myfile.c

Please keep the comma after -Wa and insert no spaces there. You can insert other options on the command lien to match those you're using in routine code, but beware of aggressive optimization: it may delete most of your skeleton code if it's doing nothing indeed.

HTH

--

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

It was the optimiser that had me concerned. In other compilers (can't remember exactly which) you can see non functional code like timimg loops completely vanish under high levels of optimisation. I could only think of two likely solutions (for this type of non-critical loop). One was to make the counter value a volatile thus insisting that the compiler assume nothing about its use; or to code the loop in assembler, out of the way of the compiler's optimisation.

It seems that HEW allows you to set different levels of optimisation for individual source files so that is another way to go I suppose.

pete Harrison

Reply to
Peter Harrison

Code such as your loop above have no effect (as far as the C virtual machine is concerned), and thus it will be removed by any optimising compiler, including gcc (when optimisations are enabled). If you want to force a delay loop in this way, the correct method is to declare "temp" to be volatile, so the compiler is unable to remove it. Examine the generated assembly code for the loop under different realistic optimisation levels (i.e., "-Os" or "-O2". Avoid more aggressive optimisations unless you really know what you are doing, as they'll probably cost in code size for very little speed gain. Occasionally, "-O1" can be helpful in debugging, but "-O0" is useless for most compilers). You'll probably find that the generated code is identical for such a simple loop, and the "volatile" on "temp" will stop the loop being removed.

Reply to
David Brown

No, that's not the way to go. Except in the case of compiler bugs (a rarity, unless your code is absurdly complicated and provokes obscure bugs), then code that fails when optimised is incorrect code. A program that only works when different parts are compiled with different optimisation flags is a maintainance nightmare.

For extremely critical timing, then assembly is the only way to do it, assuming you can't (sensibly) make use of hardware timers. For non-critical pauses like you need here, loops with volatile counters are normally the correct way to go. Alternatives such as a loop body consisting of "asm volatile ("nop")" (or whatever fits for your target) are another method.

Reply to
David Brown

Yes - good point. I had not really thought that through.

Ah - I like that a bit.

Using a volatile variable forces the compiler to include the loop and seems also to make the loop timing invariant with optimisation level.

Actually, I have been trying several simple loops and I have not yet been able to persuade GCC to eliminate an empty loop by changing optimisation levels. The code generated changes but is always there. Still, I will be happier when I can know what will be generated. At present I just set the optimisation to -O -Os. As you say, it is no good if a change in optimisation level stops things working.

Paul's link to the GCC ABI says that there are known clobbered registers for this compiler so it will not be too hard to implement and assembly-based delay that only uses the registers.

Peter Harrison

Reply to
Peter Harrison

It won't necessarily make the loop timing invariant, but it is likely to, especially for a simple loop, such as a while() loop, counting down. Sometimes a slightly more complex loop (such as a for loop) will be optimised to different code.

You'll probably find that small loops will disappear, but not large ones. When the compiler sees a 10 round loop, it might consider unrolling it and then find it does nothing, so it is eliminated. A 1000 round loop will not be unrolled, and will not get the same optimisation. But you can't be sure of this, nor can you be sure of changes in the future as the compiler gets smarter.

That's certainly possible, but unlikely to be necessary in this case (since your timing is not strict).

Reply to
David Brown

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.