On Sun, 4 May 2008 18:16:43 -0700 (PDT), Tomás Ó hÉilidhe
<toe@lavabit.comwrote in comp.lang.c:
Quote:
>
I'll try to summarise this as best I can, as my last thread wasn't
very to-the-point:
>
The C Standard says the following two things:
>
* int is the natural integer type for the system.
* int must be at least 16-Bit.
You are, perhaps unintentionally, paraphrasing the standard in a way
that appears to change the meaning. These are not two isolated
statements, but are in fact combined in one sentence.
"A ‘‘plain’’ int object has the natural size suggested by the
architecture of the execution environment (large enough to contain any
value in the range INT_MIN to INT_MAX as defined in the header
<limits.h>)."
If you think about it for a bit, the standard is highlighting the fact
that the single most important requirement for the int data type is
meeting or exceeding the range of values specified by those macros.
The first part of the sentence exists in the context of the
requirement in parentheses.
So an int is not just the natural size suggested by the architecture,
and just also, separately, must be at least 16 bits to meet the range
requirements.
The meaning of the standard is that, above all other considerations,
int must represent a minimum range of values. The type that an
implementation uses is the natural size, that is perhaps, the "best"
or "most efficient" type for meeting that primary requirement.
Quote:
Now the problem here is that these two criteria conflict if the
natural type for the system is in fact 8-Bit, which is the case with
many microcontrollers today.
One of the things that you might not realize is that the C programming
language was developed originally on a 16-bit minicomputer. At the
time Dennis was developing C, there was no such thing as an 8-bit
microprocessor in existence. Let alone 8-bit microcontrollers which
evolved after the microprocessor.
At the time of the first C compilers, I don't think there was an
architecture, and certainly not a common one, where the natural size
for an integer type was less than 16 bits.
Remember as well that the C language was not designed for embedded
systems in general, let alone for 8-bit devices in particular.
C was designed as a system programming language for minicomputers on
up through mainframes and super computers, where 16-bit processors
where the smallest kids on the block.
It was a decision by the embedded programming community to adopt C as
an embedded programming language, not any conspiracy by the "C Mafia"
or the UNIX zealots to push it on us.
C is not perfect for embedded use in many ways, even on 16- and 32-bit
hardware, but it is so vastly superior to all the alternatives that
most of us have ever seen, there is no contest.
So we make do with "ANSI standard" compilers that are missing required
data types and other parts of the language, to get what C does
provide, namely a simple and standard core language that allows
efficient code generation for the architectures that we use. Of
course "efficient" is a relative term.
If you want to know why we took to C so readily, take a look at some
of the alternatives. If I never see or write code in PL/M as long as
I live, it will be too soon. Spend a little time looking into Forth,
while you're at it.
Quote:
As an example, let's take the following code:
>
char unsigned x, y;
...
x = y;
>
On my microcontroller compiler, this produces different assembler
depending on whether 'x' is an "unsigned int" or an "unsigned char".
If it's an "unsigned char", then the assembler is:
I don't really want to be antagonistic or overly patronizing, but I
would like you to consider a few other points.
The first is that you are doing a large amount of preaching based on a
little experience with exactly one 8-bit architecture, and a severely
brain-dead one at that. Long before the PIC was developed, there were
8-bit processors such as the Z80 or 6809 which could handle 16-bit
ints much better than PIC can.
But there is another important fact that you seem to be overlooking.
It is rare, even in 8-bit embedded systems, for an entire application
to not use any values too large to fit in an unsigned char.
You have put together a small embedded system as an educational
experience, either as part of a course or on your own initiative. From
your description of the hardware on another group, it is a relatively
simple project that reads a few switches and drives a few LEDs.
In the real world, most embedded systems have more complex jobs to do,
even 8-bit ones.
You are not going to execute a PID control loop using only values in
the range 0 to 255. Nor would you be able to execute much in the way
of complex timing calculations.
The fact that, in your particular 8-bit project, you could do
everything using only the unsigned char data type is actually quite
artificial. I may have worked on a few embedded systems over the
years that never needed anything larger than 16-bit values, but I'm
fairly sure I never worked on a real world embedded system that never
needed anything larger than 8 bits.
Quote:
MOVF y, W /* Copy y to the accumulator */
MOVWF x /* Copy the accumulator to x */
>
However, if 'x' is an "unsigned int", then the assembler is:
>
MOVF y, W /* Copy y to the accumulator */
MOVWF x /* Copy the accumulator to x */
MOVF y+1, W /* Copy the next byte of y to the acc */
MOVWF x+1 /* Copy the acc to the next byte of x */
>
Now quite plainly to see, the "int" version takes twice as many
instructions in this case, and will therefore take exactly twice as
long to execute, and so will be twice as slow. In other situations,
the difference is far worse; let's take for example the following
code:
Happily, there are 8-bit architectures that have been around more than
30 years that can handle 16-bit data more efficiently.
Quote:
if (condition) x = y;
>
Depending on the type of x and y, this produces either:
>
MOVF y, W /* Copy y to the accumulator */
BTFSC condition /* If condition is false, skip the next
instruction */
MOVWF x /* Copy the accumulator to x */
>
or:
>
BTFSS condition /* Skip the next instruction if condition is true
*/
GOTO There
MOVF y, W /* Copy y to the accumulator */
MOVWF x /* Copy the accumulator to x */
MOVF y+1, W /* Copy the next byte of y to the acc */
MOVWF x+1 /* Copy the acc to the next byte of x */
There:
>
Not only does the int version consist of more instructions, but it
also involves a goto which will take up even more time. So basically
if your microcontroller is running at 8 MHz, then you may aswell
pretend it's running at 4 MHz or 2 MHz if you're going to be using int
for doing everyday arithmetic.
Now we're getting somewhere. "Everyday" arithmetic, indeed. You have
just discovered that there is often nothing "everyday" about embedded
systems.
Quote:
Now we could go down the road of discussing how C is inadequate in
terms of its accommodation of microcontrollers, but I'd rather discuss
ways of "making it right". The reason I'm so eager to bridge the gap
is that, other than the "int" situation, C is actually great for
programming an embedded system. I used it in my college project this
year to program a portable electronic Connect4 game, and it worked
great!
Here's where there's a point of view difference between you and many
in this group, not limited to me. C is not "inadequate" in
accommodating microcontrollers. No accommodation is required for
architectures like ARM and PowerPC, and even some of the 16-bit
architectures.
On the other hand, C's accommodation for 8-bit hardware can't be
described as either adequate or inadequate, because there is no C
accommodation at all for such platforms. Remember again that it was
the embedded community that decided to adopt C, not the developers of
C who targeted them.
Quote:
One way of making things right is to stop using int for arbitrarily
storing numbers, and instead use something like ufast8 (the fastest
integer type that's at least 8-Bit). In this way, neither the
microcontrollers nor the PC's suffer.
Actually, you're wrong about the fact that there is no downside to
such code on a PC.
To explain what that is, I have to lecture a bit.
You express a certain amount of indifference, if not disdain, to
professional programming. I have a feeling that you have little
understanding of professional programming in particular, and perhaps
engineering disciplines in general. Especially when it comes to what
it takes to develop safety critical and other high reliability
applications.
Who is going to do the analysis on every integer value used in a
complex operation to determine whether it will or will not ever exceed
8 bits? Who is going to write the additional test cases to prove it?
Quote:
Examples of a piece of code that could be brought between PC's and
microcontrollers is something like a look-up table as follows:
>
ufast8 days_in_month[12] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
>
To those people out there who are enthusiastic about writing portable
code, how do you feel about using types such as ufast8 instead of int?
The problems is that you are not encouraging portable programming so
much as you are encouraging people who have no relationship to 8- and
16-bit platforms to write code that might happen to more efficient
when compiled for small platforms.
I've never been a programming manager, nor do I want to be, but if I
were and I saw a programmer making the extra effort to do the analysis
and testing to do this on desktop code, Windows or Linux or Mac, I
would correct him for wasting time. And it he was doing it without
performing the extra analysis and testing, I'd correct him even more
severely. He'd be wasting time and effort on detail that would have
absolutely no benefit to the final program.
Try doing another embedded project, this time with an ARM. ST just
announced some ARM parts with up to 2MB of flash and 96KB of RAM.
Or even develop a more realistic and complex application on a PIC.
Quote:
stdint.h is great and all, but I find the type names to be too long
winded. For instance I'd rather write "ufast8" instead of
"uint_fast8_t".
That is an unfortunate attitude. The extended types defined in
stdint.h are indeed not particularly attractive, but they have the
advantage of being part of the language standard. They replace the
great balkanization of type names invented by many others in the years
before C added them.
unit_fast8_t is absolutely 100% standard. Every conforming C99
compiler must define this type in stdint.h. By now, the latest
versions of most embedded compilers include a stdint.h that defines
this alias.
If you were truly interested in portability you would use the types
that the language instead of inventing your own to suit your
esthetics.
--
Jack Klein
Home:
http://JK-Technology.Com
FAQs for
comp.lang.c
http://c-faq.com/
comp.lang.c++
http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html