EmbeddedRelated.com
Forums

Re: How to force the C-compiler to use registers?

Started by Preston Gurd May 26, 2004
Hello,

The AQ430 C compiler allows you to declare global register variables.
Although I generally suggest that people avoid this feature, it sounds
like you may have a use for it! I should also note that, if you 
declare global registers in any C source file, then you must make exactly
the same declarations in all other C source files in the application.

All the best,

Preston Gurd
(the AQ430 C compiler guy)

> 
> Dear all,
> 
> I've been wondering for a while how to avoid this enormous waste of
mem
> I presently have in the code generated from my program.
> 
> The problem I have consists of a rather complex digital logic with very
> simple algorithms.  In other words, the program is a huge bunch of
if()'s
> with
> very little maths.
> I tried to write it as modular as I could and as a result I need many
> static
> and global variables keeping flags and states.
> Of course the compiler puts them all into normal mem.
> 
> On the other hand due to the little amount of maths in the code I rarely
> need registers.  Actually R5 ... R 11 are never ever used.
> 
> Consequently, if writing assembly, I would simply put some of my
> variables into these registers.  This saves ram, and even more
> important, it saves code space, which I have very few on my 'F1101.
> Unfortunately I cannot see a possibility to get this out if the C-compiler.
> 
> Since all this was a little abstract, now for something more practical...
> Imagine some code like this:
> 
> 
> --------------------
> unsigned char Var;
> 
> main()
> {
>     SomeInitialization();
>     while (1)
>         {
>         P1OUT=Var;
>         if ( Var == 0x0F )
>             Var = 0 ;
>         }
> }
> 
> #pragma vector=PORT1_VECTOR
> __interrupt void Func1(void)
> {
>     Var |=0x01;
> }
> 
> #pragma vector=PORT2_VECTOR
> __interrupt void Func1(void)
> {
>     Var |=0x02;
> }
> 
> #pragma vector=TIMERA1_VECTOR
> __interrupt void Func1(void)
> {
>     Var |=0x04;
> }
> 
> #pragma vector=TIMERA2_VECTOR
> __interrupt void Func1(void)
> {
>     Var |=0x08;
> }
> 
> --------------------
> 
> Here you can easily imagine, that each access to Var wastes 2 Bytes of mem
> compared
> to if it was in a register.
> As I wrote earlier there are 7 registers which are potentially usable for
> globals and maybe
> 6 different accesses for each of these variables.
> 7 times 6 times 2 is        84       Bytes of wasted mem, which again is
> more than 8 percent of
> the entire mem available.
> 
> This again, in my opinion, is definitely too much.
> 
> So, has anyone got a smart idea how to get out of this?
> 
> 
> Regards,
> Dirk
> 
> 
> 
> 
> 
> .
> 
>  
> Yahoo! Groups Links
> 
> 
> 
>  

Beginning Microcontrollers with the MSP430

Dear all!

Firstly, thanks to all of you for the help on my previous request.
The results were quite interesting, so I'd like to share them.
For other newbies on the one hand encountering the same problems
and for the compiler-makers here on the other as a little feedback
from the market.  :-)

The application:
MSP430F1101  (1K Flash-ROM)
Mostly simple independent control tasks.  All with independent timings.
The code mostly consists of flags and many if{}else{} blocks.

After shortly thinking about the requirements 'C' turned out to be a
must.  Of course, assembler is always the best solution to get the
optimum result for a small isolated problem.

But in this cases the program has to be:
-readable, after years, even for others that don't speak asm.
-devided into OS, HAL, and API.  So all the core algorithms are
 entirely harware-independent and independent from each other.


If designing such an architecture in assembler with all 3 layers
would require many CALL's which again are a waste of mem.
Some inlining might be possible using macros which again leads
only half way to goal but makes the code entirely unreadable.

Note to the never-ending discussion about asm vs. C:
This is a good example where C really performs better that asm.

Unfortunally C has some constrains, particularly on the use of
registers, which is not entirely effective in this case.
(This was the beginning of this thread - r4..r11 where never ever
used, but RAM-based variables instead.)

This is my starting point with quite a lot of functions realized
and only 900 bytes of code used  with the IAR compiler.  Of course
this already required many cycles of manual optimization of the C-code.
But looking at the asm output I could only find very few situations,
where I could imagine a better code written in pure asm.
Except for this register thing.  The asm-output had a potential of
another 100 Bytes to be saved.


Asking the list for help brought 3 ideas:

1) take the asm-output and do a manual find-an-replace on the related
  variables

2) IAR in the latest version allows at least R4 to be used globally

3) Quadravox can to with registers whatever you what.


For the moment 2) was unreachable for me $$$.

So I went with 3).
A short test with one of my core modules showed that Quadravox
really has the potential to reduce the size of this module
from 140 (IAR)  to  90 Bytes.

Porting was really only a matter of hours.  But the first shock was
deep...  Quadravox made code of 1900 bytes.
reason a: Quadravox can do inlining but by far not as elegant as IAR.
reason b: Quadravox seems to have no dead-code-elimination at all.
if (FALSE) { so_something() }   is actually generated byte by byte.

After optimizing the code for Quadravox I was able to get the code
shrunk to 1100 Bytes.  The rest was mostly ineffective inlining.

I could do the next step as well and remove the requirement for
inlining from the code but then I loose all portability and modularity.
At the end I would still use C but had to write the code in a style
that is mostly assembler.



Comparision on a function that checks an input pin which shall be
inlined into a higher level function:


IAR does:
         BIT.B  #0x01, &0x20
         JNE    ....

Quadravox does:
          MOV.B  &0x20, R12
          MOV.B  #0x01, R13
          AND.B  R12, R13
          JNE    ...


Quadravox sometimes even does:
          MOV.B  &0x20, R12
          AND.B  #0x01, R12
          AND.B  #0xFF, R12
          JNE    Label1
          MOV.W  #0x01, R13
          JMP    Label2
Label1:
          MOV.W  #0x00, R13
Label2:
          AND.W  #0xFF, R13
          JNE    ....

----------------------------------
Conclusion:

The optimum code can always be achieved with assembler, with the known
disadvantages.
-> 800 Byte

IAR does a pretty good optimization and fails only on the
register-usage
hign level C-code -> 900 Byte

Quadravox lacks a good optimizer, but can do the registers .

hign level C-code -> 1800 Byte
not so high level -> 1100 Byte
register usage    -> 1000 Byte


So my wish would be:

IAR - please add a register feature like QA430 does.

Quadravox - please improve your optimizer  (inlining, dead-code).

If both of you did so, we had two extremely effective compilers for
our MSP430.



Regards,
Dirk


Having read this rather lengthy post the only comment I have is that it 
reinforces my personal beliefe that C is no faster than asm. You state 
that you went through several optimisation cycles with C, then tried 
Quadravox, and again jumped throughb hops trying to optimise it.

Your reason that C is 'a must' is given as the need to implement an
OS, 
HAL and API. Sorry, these are just fancy acronyms for stuff that we do 
all the time, nothing hard about creating these in ANY language. The 
time you wasted optimising your C source would have taken far longer 
than writing the original in asm.

You also bring up the old furphy6 of readability. Most peoples C sourcre 
is as readable as tea leaves in the bottom of a cup. Asm can be as 
legible, or illegible as C, it depends on the author. C itself changes 
quite significantly over time (witness the long and winding thread with 
Rolf wrt C90/C99 and "the C standard") asm doesn't. Unless the
life of 
your product is expected to exceed 20+ years, requiring regular 
maintenance updates, without a micro update, I wouldn't worry about it. 
I still have references (and chips in most cases) going back to the 
4004. In fact I have many parts, including EPROM parts, for micros that 
were never even generally available.

Of course I'm well known as a neanderthal.

Al

Dirk Reymann wrote:
> Dear all!
> 
> Firstly, thanks to all of you for the help on my previous request.
> The results were quite interesting, so I'd like to share them.
> For other newbies on the one hand encountering the same problems
> and for the compiler-makers here on the other as a little feedback
> from the market.  :-)
> 
> The application:
> MSP430F1101  (1K Flash-ROM)
> Mostly simple independent control tasks.  All with independent timings.
> The code mostly consists of flags and many if{}else{} blocks.
> 
> After shortly thinking about the requirements 'C' turned out to
be a
> must.  Of course, assembler is always the best solution to get the
> optimum result for a small isolated problem.
> 
> But in this cases the program has to be:
> -readable, after years, even for others that don't speak asm.
> -devided into OS, HAL, and API.  So all the core algorithms are
>  entirely harware-independent and independent from each other.
> 
> 
> If designing such an architecture in assembler with all 3 layers
> would require many CALL's which again are a waste of mem.
> Some inlining might be possible using macros which again leads
> only half way to goal but makes the code entirely unreadable.
> 
> Note to the never-ending discussion about asm vs. C:
> This is a good example where C really performs better that asm.
> 
> Unfortunally C has some constrains, particularly on the use of
> registers, which is not entirely effective in this case.
> (This was the beginning of this thread - r4..r11 where never ever
> used, but RAM-based variables instead.)
> 
> This is my starting point with quite a lot of functions realized
> and only 900 bytes of code used  with the IAR compiler.  Of course
> this already required many cycles of manual optimization of the C-code.
> But looking at the asm output I could only find very few situations,
> where I could imagine a better code written in pure asm.
> Except for this register thing.  The asm-output had a potential of
> another 100 Bytes to be saved.
> 
> 
> Asking the list for help brought 3 ideas:
> 
> 1) take the asm-output and do a manual find-an-replace on the related
>   variables
> 
> 2) IAR in the latest version allows at least R4 to be used globally
> 
> 3) Quadravox can to with registers whatever you what.
> 
> 
> For the moment 2) was unreachable for me $$$.
> 
> So I went with 3).
> A short test with one of my core modules showed that Quadravox
> really has the potential to reduce the size of this module
> from 140 (IAR)  to  90 Bytes.
> 
> Porting was really only a matter of hours.  But the first shock was
> deep...  Quadravox made code of 1900 bytes.
> reason a: Quadravox can do inlining but by far not as elegant as IAR.
> reason b: Quadravox seems to have no dead-code-elimination at all.
> if (FALSE) { so_something() }   is actually generated byte by byte.
> 
> After optimizing the code for Quadravox I was able to get the code
> shrunk to 1100 Bytes.  The rest was mostly ineffective inlining.
> 
> I could do the next step as well and remove the requirement for
> inlining from the code but then I loose all portability and modularity.
> At the end I would still use C but had to write the code in a style
> that is mostly assembler.
> 
> 
> 
> Comparision on a function that checks an input pin which shall be
> inlined into a higher level function:
> 
> 
> IAR does:
>          BIT.B  #0x01, &0x20
>          JNE    ....
> 
> Quadravox does:
>           MOV.B  &0x20, R12
>           MOV.B  #0x01, R13
>           AND.B  R12, R13
>           JNE    ...
> 
> 
> Quadravox sometimes even does:
>           MOV.B  &0x20, R12
>           AND.B  #0x01, R12
>           AND.B  #0xFF, R12
>           JNE    Label1
>           MOV.W  #0x01, R13
>           JMP    Label2
> Label1:
>           MOV.W  #0x00, R13
> Label2:
>           AND.W  #0xFF, R13
>           JNE    ....
> 
> ----------------------------------
> Conclusion:
> 
> The optimum code can always be achieved with assembler, with the known
> disadvantages.
> -> 800 Byte
> 
> IAR does a pretty good optimization and fails only on the
> register-usage
> hign level C-code -> 900 Byte
> 
> Quadravox lacks a good optimizer, but can do the registers .
> 
> hign level C-code -> 1800 Byte
> not so high level -> 1100 Byte
> register usage    -> 1000 Byte
> 
> 
> So my wish would be:
> 
> IAR - please add a register feature like QA430 does.
> 
> Quadravox - please improve your optimizer  (inlining, dead-code).
> 
> If both of you did so, we had two extremely effective compilers for
> our MSP430.
> 
> 
> 
> Regards,
> Dirk
> 
> 
> 
> 
> .
> 
>  
> Yahoo! Groups Links
> 
> 
> 
>  
> 
> 


Hi Dirk,

I also used the Quadravox compiler, but found the code generated 
quite good. And if it wasn't, Preston "the AQ430 C compiler 
guy" usually was very fast in improving the code generator.
Did you use a recent version of AQ430?

I generally don't rely on compiler optimization - perhaps because I 
grew up with non-optimizing compilers.
I almost never use inlining, but preprocessor macros instead.
For dead-code-elimination, I use conditional compilation - or just 
don't write dead code.

You didn't post your inlined function, perhaps it looks like this:
int P10_Low(void) { return (P1IN & 0x01) == 0x00;}
or
int P10_Low(void) { return (P1IN & 0x01) ? FALSE : TRUE;}

You may want to try
#define P10_Low() (!(P1IN & 0x01))
I did't test it, but I'm pretty sure the generated code the code will 
be better. And it's just as readable and portable as the inlined 
version.

Regards,
Wolfgang


> Comparision on a function that checks an input pin
which shall be
> inlined into a higher level function:
> 
> 
> IAR does:
>          BIT.B  #0x01, &0x20
>          JNE    ....
> 
> Quadravox does:
>           MOV.B  &0x20, R12
>           MOV.B  #0x01, R13
>           AND.B  R12, R13
>           JNE    ...
> 
> 
> Quadravox sometimes even does:
>           MOV.B  &0x20, R12
>           AND.B  #0x01, R12
>           AND.B  #0xFF, R12
>           JNE    Label1
>           MOV.W  #0x01, R13
>           JMP    Label2
> Label1:
>           MOV.W  #0x00, R13
> Label2:
>           AND.W  #0xFF, R13
>           JNE    ....
> 



Hi Al!

[Al wrote:]
> Having read this rather lengthy post the only
comment I have is that it
> reinforces my personal beliefe that C is no faster than asm. You state
> that you went through several optimisation cycles with C, then tried
> Quadravox, and again jumped throughb hops trying to optimise it.

> Your reason that C is 'a must' is given
as the need to implement an OS,
> HAL and API. Sorry, these are just fancy acronyms for stuff that we do
> all the time, nothing hard about creating these in ANY language. The
> time you wasted optimising your C source would have taken far longer
> than writing the original in asm.
> [...]
> Of course I'm well known as a neanderthal.

:-)
Of course, I already noticed, that you are one of the assembler-guys.

Furthermore, you are absolutely right.  Generally, C is slower (writing and
executing) than asm, as well as C++ is slower than C.

One of the reasons is that the higher evolved language (if you
*really* utilize the additional features) requires writing much more
text to achieve the same result.  I think, C++ is the best example:
You can produce KB's of source-code without generating a single
opcode.

The very good reason for this is, that you give a lot more information
about what you are going to achieve to the compiler, so it can check
by itself if you did it correctly.
E.g. variable types (unsigned int, const int, etc.)  are entirely
superfluous, if you just want to get your program up and running
quickly.  But it is an enourmous help to ensure that you did not make
any errors in your concept.
(BTW:  In my opinion EVERY type-case in your program is an indicator
that your concept is inconsistent!)


It's the same with my OS, HAL and API.  These are just constructs to
partition my thoughts.   I write the C code exactly this way, i.e. in
a way that directly represents my abstract view of the system.

Then comes the compiler, or better to say, the optimizer in place.
It recognizes, that most of these constructs are very ineffective from
the machines point of view.  So it throws all my layers and functions
away  and leaves only the necessary code.

My C program has 3 or 4 independent layer, handles massages between
tasks, an includes lots and lots of function calls.
This way of writing it makes the program obvious and portable.

The compiler then shrinks all this to pure spaghetti-code with only
one single CALL statement left (where it really is more effective to
leave the CALL).


Now, again, I've written lots of text.  Maybe I'd better come to the
point:

You can can write every program in two ways:
a) highly abstract code, which is inefficient, but flexible.
b) spaghetti-code, which is very lean, short and fast.  Maybe you can
still read it, but it is never flexible!

The you can use either asm or a high level language.

Asm might be faster to finish the program, but you always get exactly
the program you wrote.  You write b), you get b) ... you write a) you
get a).

The great achievement of C is, that it can transform and a) program
to a b) program.


I have many KiloBytes of C-source-code and get 800 bytes after
compilation.

If I had written the same API, HAL and so on in assembler, the program
would be at least 2000 bytes.

If had written the spaghetti code in assembler i would probably only
have 700 bytes.  But I would neither have a HAL, nor an OS, or
anything else.
None of my colleagues would be able to read the program and if I
tried to port it to another architecture or if I tried to get one
the the core algorithms out of it, I would have to start all over
again.


'Hope I could make myself clear this time.
'But always open for discussions.

Regards,
Dirk


Hi Wolfgang!

> I also used the Quadravox compiler, but found the
code generated
> quite good. And if it wasn't, Preston "the AQ430 C compiler 
> guy" usually was very fast in improving the code generator.
> Did you use a recent version of AQ430?

Don't know, how recent it was.  I just downloaded the eval-version
4 days ago.
Oh, by the way... Preston, if you read this:
Starting the IDE and then loading the project is no problem.
But starting the IDE by double-clicking the project file and then
compiling lets the compiler freeze.  Might be a bug?

> I generally don't rely on compiler
optimization - perhaps because I 
> grew up with non-optimizing compilers.
> I almost never use inlining, but preprocessor macros instead.
> For dead-code-elimination, I use conditional compilation - or just 
> don't write dead code.

> You didn't post your inlined function,
perhaps it looks like this:
> int P10_Low(void) { return (P1IN & 0x01) == 0x00;}
> or
> int P10_Low(void) { return (P1IN & 0x01) ? FALSE : TRUE;}

> You may want to try
> #define P10_Low() (!(P1IN & 0x01))
> I did't test it, but I'm pretty sure the generated code the code
will
> be better. And it's just as readable and portable as the inlined 
> version.


Sorry Wolfgang,  this is *exactly* what I was trying to avoid when I
chose C for my project.
Maybe you read my recent mail to Al.  The ONLY reason (for me) to use
the compiler is that it does optimization.  The approach you described
basically means:
Use the language C, but write the program in assembler style.
If you do this, you combine the disadvantages of both languages.  In
that case Al would be absolutely right.  Assembler is faster, leaner
and as easily to read as your C-code.

On the other hand you are right.  Many of my inlined functions look as
you guessed.  And yes, I also replaced some of them with macros as
you suggested.  In fact, using the macro saves 2 bytes with IAR and
about 12 Bytes (estim.) with Quadravox.


But as I wrote earlier, I'm mostly using C because I want to design my
code in a highly abstract way and then let the compiler do the
shrinking.


Example:

inline int IsVoltageAboveTheshold(void)
{
    CACTL = SOME_PATTERN;   // set up the comparator
    _NOP();                 // wait for settling
    return (!(CACTL & CAOUT));
}


inline int Signal(int state)
{
    if ( state == -1 )
       P1OUT &= ~ BIT2;
       
    if ( state == 1 )
       P1OUT |= BIT2;
       
    return ( P1OUT & BIT2 );
}

void main(void)
{
    if ( IsVoltageAboveTheshold() )
      Signal( 1 );
}


The main function is written first.
At this point I don't care about hardware or bits or registers at all.
It is high level.  All I want to "say" is: If my voltage is above a
threshold, some signal shall be HI.

Maybe there are other places in the program where I set this Signal
and others where I want to get the state.  Therefore
int Signal(int state);       would be a clear and lean interface.

What this signal actually is and how it is accessed, is defined later
in a different layer in a different module/file.  Maybe I also later
decide, that I do something with the PWM-generator to realize my
signal.

So you see:  main() is high-level,  Signal() and
IsVoltageAboveTheshold() are low-level with a very clean interface in
between.

And, lucky me, the IAR-compiler makes extremely good code out of this.
unfortunately Quadravox doesn't.

But to be honest, since I was very satisfied with IAR's code output
I didn't think much about "tricks" to shrink this.

--------------------------------
I'm looking forward to any suggestions how to "improve" the
inlined
functions above.
Do NOT change main().
--------------------------------


Regards,
Dirk




PS.:
Just for those, who are interested in what the compilers make out of
this program:

IAR...
main()
{
    CACTL = SOME_PATTERN;
    _NOP();
    if ( !(CACTL & CAOUT) )
        P1OUT |= BIT2;
)


Quadravox...
main()
{
    CACTL = SOME_PATTERN;
    _NOP();
    int x1= !(CACTL & CAOUT) ;
    if ( x1 )
    {
        int x2 = 1;
        if ( x2 == -1 )
           P1OUT &= ~ BIT2;
       
        if ( x2 == 1 )
            P1OUT |=    BIT2;

        int x3 = P1OUT & BIT2;
    }
}


Dirk Reymann wrote:
> Hi Al!
> 
> [Al wrote:]
> 
>>Having read this rather lengthy post the only comment I have is that it
>>reinforces my personal beliefe that C is no faster than asm. You state
>>that you went through several optimisation cycles with C, then tried
>>Quadravox, and again jumped throughb hops trying to optimise it.
> 
> 
>>Your reason that C is 'a must' is given as the need to
implement an OS,
>>HAL and API. Sorry, these are just fancy acronyms for stuff that we do
>>all the time, nothing hard about creating these in ANY language. The
>>time you wasted optimising your C source would have taken far longer
>>than writing the original in asm.
>>[...]
>>Of course I'm well known as a neanderthal.
> 
> 
> :-)
> Of course, I already noticed, that you are one of the assembler-guys.

Not strictly true, I've written code in juts about every HLL going, any 
many long since gone. I use C predominantly on Pc's, but just do less PC 
based work these days. I have yet, after well over 30 years, to find a 
good reason to use C on small (sub 64k) embedded systems. I have done so 
in the past, but found the gains to be far outweighed by the negatives.

> 
> Furthermore, you are absolutely right.  Generally, C is slower (writing and
> executing) than asm, as well as C++ is slower than C.
> 
> One of the reasons is that the higher evolved language (if you
> *really* utilize the additional features) requires writing much more
> text to achieve the same result.  I think, C++ is the best example:
> You can produce KB's of source-code without generating a single
> opcode.
> 
> The very good reason for this is, that you give a lot more information
> about what you are going to achieve to the compiler, so it can check
> by itself if you did it correctly.

And you would prefer to rely on the compiler vendors skills than your 
own? personally I have more trust in what I create than how others might 
  choose to interpret it.

> E.g. variable types (unsigned int, const int,
etc.)  are entirely
> superfluous, if you just want to get your program up and running
> quickly.  But it is an enourmous help to ensure that you did not make
> any errors in your concept.
> (BTW:  In my opinion EVERY type-case in your program is an indicator
> that your concept is inconsistent!)

And I consider them an extremely powerful tool. I regularly utilise a 16 
bit value, then treat it a byte at a time. I ahve several algorithms I 
use which rely on this, and which, without this simple artefact would 
take longer to process than I can afford. This isn't inconsistency, it 
is simply recognising at design time that there is a very valuable short 
cut to be gained by NOT using rigid type casting (a thing I truly 
detest). I have a very compute intensive application, for example that 
uses only integer logic, but the format of the calculations differs 
regularly, for example I might use 1:15 format in one place , pull a 
result in 3:29 format, then grab a slice from that which is effectively 
a different format. Why, well, instant scaled calculation, filters in a 
single clock, etc etc.

> 
> 
> It's the same with my OS, HAL and API.  These are just constructs to
> partition my thoughts.   I write the C code exactly this way, i.e. in
> a way that directly represents my abstract view of the system.

I partition my thoughts somewhat differently, I simply use separate 
modules for each code'entity'. Everything to do with LCD graphics sits

in GRAPHICS.s43, whereas the low level handling of the LCD panel sits in 
LCD.s43. Crude, simple, and extremely effective for many years. I now 
pull LCD.s43 from project XYZ-1997, and bolt it into project 123-2004, 
make the slight changes needed to handle a colour panel instead of a 
black and white panel, and I have 80% pre-tested code ready to roll. it 
now becomes CLCD.s43. next I took Graphics.s43 and created CGraphics.s43 
(took under an hour to modify both, and worked beautifully first shot)

Each module is VERY tight, adequately commented, easily modified, easily 
re-used, and very simple to understand. My view of the system is not 
abstract, it has solidity from the moment I commit to coding it, yet all 
the flexibility necessary.

> 
> Then comes the compiler, or better to say, the optimizer in place.
> It recognizes, that most of these constructs are very ineffective from
> the machines point of view.  So it throws all my layers and functions
> away  and leaves only the necessary code.

Seemingly making it hard for you to relate the concept to the practical 
implementation?

> 
> My C program has 3 or 4 independent layer, handles massages between
> tasks, an includes lots and lots of function calls.

So it is simply a typical program. Depending on what you call a layer. 
The modern misuse differs considerably from what I would consider a 
software 'layer' to be.

> This way of writing it makes the program obvious
and portable.

Portability is not a property of C. There are always differences between 
compilers and versions of compilers that need to be taken into account. 
Portability is a myth, I can port code from a HC11 to an MSP430 faster 
than you could study the differences between compiler A and compiler B, 
and perform on the fly optimisation.

> 
> The compiler then shrinks all this to pure spaghetti-code with only
> one single CALL statement left (where it really is more effective to
> leave the CALL).

And you enjoy debugging this?

> 
> 
> Now, again, I've written lots of text.  Maybe I'd better come to
the
> point:
> 
> You can can write every program in two ways:

You can write ANY program in a thousand ways ate least.


> a) highly abstract code, which is inefficient, but
flexible.

or a highly abstract code which is neither of the above

> b) spaghetti-code, which is very lean, short and
fast.  Maybe you can
> still read it, but it is never flexible!

Why not? And you omit non-spaghetti hand crafted assembler that is clear 
and clean to read. I would like to believe, and there are lots of posted 
examples for you to look at, that my assembler is no where near as 
obfuse as typical C. Heavily optimised code may not be obvious at a 
casual glance, but that's what comments are for.

> 
> The you can use either asm or a high level language.

Of course there are a million opinions about this, non of which is 
correct. The real answer is use what you are comfortable with, that does 
the job. For me that is assembler 99% of the time.

> 
> Asm might be faster to finish the program, but you always get exactly
> the program you wrote.  

Well of course, that is the intention. I WANT to get what I wrote, that 
way I know it will function correctly.

You write b), you get b) ... you write a) you
> get a).
> 
> The great achievement of C is, that it can transform and a) program
> to a b) program.

This is not a great achievement, it is a diabolical disaster. Why do I 
want it to give me b if I wrote a. Had I wanted b I would have written 
it in the first place.

> 
> 
> I have many KiloBytes of C-source-code and get 800 bytes after
> compilation.
> 
> If I had written the same API, HAL and so on in assembler, the program
> would be at least 2000 bytes.

Please don't be offended, but this is probably more a personal thing 
than a technical issue, some people cannot think in asm, even if they 
can write it, to write good asm I believe you need to not only think in 
it, but to think in terms of the physical hardware function as well, ie 
what else is happening, what side effects might be useful to me, how can 
I use that to my advantage. I like hardware, I don't want to abstract 
myself from it.

practically speaking I have never had to port one of my own designs to a 
differnet micro , and I know  for a fact that systems I designed over 20 
years ago are still in every day commercial use, and that designs from 
the pre single chip micro days, ie wholly logic based computer designs 
are still in use moe than 30 years after I designed them. Portability is 
NOT an issue for me, efficient functionality is.

> 
> If had written the spaghetti code in assembler i would probably only
> have 700 bytes.  But I would neither have a HAL, nor an OS, or
> anything else.

Why not, and why do you believe it would be spaghetti code? I manage to 
write efficient tight highly modular code, as do many others. I don't 
eat pasta.

> None of my colleagues would be able to read the
program and if I
> tried to port it to another architecture or if I tried to get one
> the the core algorithms out of it, I would have to start all over
> again.

I mostly post code samples here to beginners. All in asm. So far nobody 
has complained that they are illegible, or couldn't be understood. I 
hope that isn't simply because they are afraid I might over react. 
certainly the same would appear to be true for the other guys here who 
regularly post code. Even I can read their C (though I hate the 
bastardisation used to make C embedded friendly) and the Op's appear to 
be able to read their asm posts as well. It doesn't seem to me to be a 
problem.

> 
> 
> 'Hope I could make myself clear this time.
> 'But always open for discussions.

The main game is to enjoy what you do. As long as it's fun you'll 
usually do it well. Once it becomes drudgery your results will reflect 
that. I take what I do very seriously, and I do some serious stuff, but 
I still refer to my office as the toy room, and new projects as my 
latest toys, no matter how serious they might be.

Cheers

Al


Hi Al!

> And you would prefer to rely on the compiler
vendors skills than your
> own? personally I have more trust in what I create than how others might
>   choose to interpret it.

No, not at all.  If I'd rely on them I'd not post on this board. :-)
Actually half the time writing code is spent having an eye on the
compiler's output.
But I never claimed writing C to be fast.


>> Then comes the compiler, or better to say, the
optimizer in place.
>> It recognizes, that most of these constructs are very ineffective from
>> the machines point of view.  So it throws all my layers and functions
>> away  and leaves only the necessary code.

> Seemingly making it hard for you to relate the
concept to the practical
> implementation?
> [...]
> Well of course, that is the intention. I WANT to get what I wrote, that
> way I know it will function correctly.
>> [optimizer transforms programs]
> This is not a great achievement, it is a diabolical disaster. Why do I
> want it to give me b if I wrote a. Had I wanted b I would have written
> it in the first place.

YESSS!    That exactly is my point.
I'm human.  I think human.
The MSP is a machine!

Why the heck should I have to adapt to an entirely different world?
Of course I can do, but this is just an extra risk of making errors
or at least to be inefficient.

I'm German.  I've learnt English.
But if wanted to write a great poem I'd never dare to try this in
English.  I'd do it in my mother-tongue and rely on an expert to
translate it.

I've heard of people having invented something called
"object oriented programming" for exactly this reason:
A human describes a problem and it's solution in a human way
from a human perspective.  Afterwards a proven and optimized
machine transforms this description to something a machine can
work with.

>> I have many KiloBytes of C-source-code and get
800 bytes after
>> compilation.
>> If I had written the same API, HAL and so on in assembler, the program
>> would be at least 2000 bytes.

> Please don't be offended, but this is
probably more a personal thing
> than a technical issue, some people cannot think in asm, even if they
> can write it, to write good asm I believe you need to not only think in
> it, but to think in terms of the physical hardware function as well, ie
> what else is happening, what side effects might be useful to me, how can
> I use that to my advantage. I like hardware, I don't want to abstract
> myself from it.

I could think in asm, and I absolutely have to at least when I design
the HAL.
But in all layers above that I don't want to care at all, if this
algorithm is run on an MSP, a PC, my coffee-machine or fed into "The
Matrix".
;-)  BTW:  Why would the machines have had to build The Matrix?
They could have fed the humans in their breeding tanks with opcodes
as well?  But for some reason this seems not to have worked.  :-)


If I reduce the problem to just two layers.
One is the HAL. The other is my application-layer which shall
know nothing of any hardware.   Writing in C I would put them into
two different files.
I can do so in asm as well.  But is this really a good idea.

Al, please have a look at my posting to Wolfgang.
Imagine main() in one file,  IsVoltageAboveTheshold() and Signal() in
a different file.
How do you want to achieve such an architecture with pure asm code?
How would such asm-code react on changes?

>> The compiler then shrinks all this to pure
spaghetti-code with only
>> one single CALL statement left (where it really is more effective to
>> leave the CALL).

> And you enjoy debugging this?

OK!   That's a drawback.
Debugging actually is a pain in the ass.  Simply because the
compiler's output has nothing to do with it's input any more.

On the other hand, I can switch of optimization and use a larger
flash during development.  This again leads to the old problem,
that the debug-build works fine and the final release-build is
buggy.


Regards,
Dirk

PS:  Two things to add:

You're right in one point.  If the compiler is poor all I've written
is useless.  Fortunately there ARE good compilers.

I believe our main difference in this discussion is that you think in
registers and bits and aim to a concrete realization of a program.
I think more of an abstract description of what the system shall do,
without caring where what signal is going to be connected.


Ok, you asked for it ;-)

> inline int IsVoltageAboveTheshold(void)
> {
>     CACTL = SOME_PATTERN;   // set up the comparator
>     _NOP();                 // wait for settling
>     return (!(CACTL & CAOUT));
> }

#define IsVoltageAboveTheshold() ( \
     CACTL = SOME_PATTERN, \
     _NOP(), \
     !(CACTL & CAOUT))

BTW: are you sure that one _NOP() is enough for comparator settling?


> inline int Signal(int state)
> {
>     if ( state == -1 )
>        P1OUT &= ~ BIT2;
>        
>     if ( state == 1 )
>        P1OUT |= BIT2;
>        
>     return ( P1OUT & BIT2 );
> }

#define Signal(state) ((state==-1 ? P1OUT &= ~BIT2 : (state==1 ? 
P1OUT |= BIT2 : P1OUT)) & BIT2)

I don't claim that this is readable - not at all. I'd rather say
it's 
terrible. But I like it.

> void main(void)
> {
>     if ( IsVoltageAboveTheshold() )
>       Signal( 1 );
> }

<snip>

> --------------------------------
> I'm looking forward to any suggestions how to "improve" the
inlined
> functions above.

Don't know if it's an improvement. It's different.

> Do NOT change main().
> --------------------------------

I didn't :-)

Wolfgang



Of course you know that this could be an endless debate ;@}

Dirk Reymann wrote:
> Hi Al!
> 
> 
>>And you would prefer to rely on the compiler vendors skills than your
>>own? personally I have more trust in what I create than how others might
>>  choose to interpret it.
> 
> 
> No, not at all.  If I'd rely on them I'd not post on this board.
:-)
> Actually half the time writing code is spent having an eye on the
> compiler's output.
> But I never claimed writing C to be fast.

I noticed, unusually so, this is often one of the claims made for using 
it over asm.

> 
>>Seemingly making it hard for you to relate the concept to the practical
>>implementation?
>>[...]
>>Well of course, that is the intention. I WANT to get what I wrote, that
>>way I know it will function correctly.
>>
>>>[optimizer transforms programs]
>>
>>This is not a great achievement, it is a diabolical disaster. Why do I
>>want it to give me b if I wrote a. Had I wanted b I would have written
>>it in the first place.
> 
> 
> YESSS!    That exactly is my point.
> I'm human.  I think human.
> The MSP is a machine!

And the compiler writers are human, (well some of them are ;@} ) and 
therefore equally prone to error. The MSP doesn't think, only the 
programmer does. The MSP is created by humans, and therefore, one would 
think, constructed in a manner simply understood by humans, such that 
its operational processes may become clear and obvious with a little 
study. A car is not human, but it is a simple task to learn to control 
one. (However, just like in programming, there are many who believe they 
are in control of the car, when they are clearly driving beyond their 
abilities ;@} ) You seem to have adopted the rather odd position of 
wanting to be chauffeur driven, ie relying upon soeone else to provide 
the necessary control.


> 
> Why the heck should I have to adapt to an entirely different world?
> Of course I can do, but this is just an extra risk of making errors
> or at least to be inefficient.

Of course it isn't a different world, the micro is simply a human 
construct, designed by human minds, to simplify human bound tasks. At 
the end of the day it "knows" only two things, on and off, thus anyone

who can differentiate these two things can become a master of the machine.

> 
> I'm German.  I've learnt English.

I'm English, I lived in Germany, and found that my skill at speaking 
German increased alongside my consumption of alcohol, unlike my 
programming, which suffers as my alcohol intake increases. Thus 
comparing the two 'languages' is invalid ?

> But if wanted to write a great poem I'd never
dare to try this in
> English.  I'd do it in my mother-tongue and rely on an expert to
> translate it.

But this is the exact point, no matter how great the outside expert the 
poem simply wouldn't translate, and be as great. Poetry derives its 
essence from many things, including rhyme, or a set cadence, or a set 
pattern, it rarely, if ever translates across language barriers. For 
example the poem

The boy stood on the burning deck
Playing with fire crackers
One flew up his trouser leg
and blew off both his knackers

Simple ceases to rhyme in German, and thus loses its point.

> 
> I've heard of people having invented something called
> "object oriented programming" for exactly this reason:
> A human describes a problem and it's solution in a human way
> from a human perspective.  Afterwards a proven and optimized
> machine transforms this description to something a machine can
> work with.

Objected oriented programming is about as far from this description as 
you can get. It takes something as elegant as the human thought process 
and churns it into an elephant. (an elephant being a mouse designed by a 
committee)

> 
>>>I have many KiloBytes of C-source-code and get 800 bytes after
>>>compilation.
>>>If I had written the same API, HAL and so on in assembler, the
program
>>>would be at least 2000 bytes.
> 
> 
>>Please don't be offended, but this is probably more a personal
thing
>>than a technical issue, some people cannot think in asm, even if they
>>can write it, to write good asm I believe you need to not only think in
>>it, but to think in terms of the physical hardware function as well, ie
>>what else is happening, what side effects might be useful to me, how can
>>I use that to my advantage. I like hardware, I don't want to
abstract
>>myself from it.
> 
> 
> I could think in asm, and I absolutely have to at least when I design
> the HAL.
> But in all layers above that I don't want to care at all, if this
> algorithm is run on an MSP, a PC, my coffee-machine or fed into "The
> Matrix".
> ;-)  BTW:  Why would the machines have had to build The Matrix?
> They could have fed the humans in their breeding tanks with opcodes
> as well?  But for some reason this seems not to have worked.  :-)
> 
> 
> If I reduce the problem to just two layers.
> One is the HAL. The other is my application-layer which shall
> know nothing of any hardware.   Writing in C I would put them into
> two different files.
> I can do so in asm as well.  But is this really a good idea.
> 
> Al, please have a look at my posting to Wolfgang.
> Imagine main() in one file,  IsVoltageAboveTheshold() and Signal() in
> a different file.
> How do you want to achieve such an architecture with pure asm code?
> How would such asm-code react on changes?
> 

I wouldn't. By the time I sit and write ANY code I have already 
determined the relevant parameters of my design. There is nothing 
abstract, or unknown, or to be discovered. I find this approach a little 
like stepping off a cliff and hoping to hell that my arms flap fast 
enough. If you simply wanted to implement the logic of this without 
commiting speciifc resources that is an easy enough thing to do, but I 
simply never approach this phase of a design with any abstraction in 
mind. This has all been filtered out at the designstage, which is 
largely cerebral, but which also contains notes on any critical factors, 
such as timing, or signal levels.

> 
>>>The compiler then shrinks all this to pure spaghetti-code with only
>>>one single CALL statement left (where it really is more effective to
>>>leave the CALL).
> 
> 
>>And you enjoy debugging this?
> 
> 
> OK!   That's a drawback.
> Debugging actually is a pain in the ass.  Simply because the
> compiler's output has nothing to do with it's input any more.

Not at all. The "no notes, top down, figure it out as I go along" 
approach will inherently lead to difficult to debug code, since nothing 
has been pre-planned, including the most crucial part of the system 
functional verification, or spec compliance. How can this be planned for 
when the program itself has not been designed, simply brewed up from a 
hodge podge of vague notions.

Effective debugging requires effective design and pre-planning. It is 
nothing to do with the compiles output. Pre-designing for test can be 
done in any language. This usually results in pain free debugging, and 
design verification.

> 
> On the other hand, I can switch of optimization and use a larger
> flash during development.  This again leads to the old problem,
> that the debug-build works fine and the final release-build is
> buggy.
> 
> 
> Regards,
> Dirk
> 
> PS:  Two things to add:
> 
> You're right in one point.  If the compiler is poor all I've
written
> is useless.  Fortunately there ARE good compilers.
> 
> I believe our main difference in this discussion is that you think in
> registers and bits and aim to a concrete realization of a program.
> I think more of an abstract description of what the system shall do,
> without caring where what signal is going to be connected.

I believe that we have intrinsically different design philosophies. I 
think about as abstractly as it gets but ultimately thought without 
purpose is wasted thought. The very early stages of solving a problem 
may be abstract, but defining the solution can never be.

Cheers

Al