Searching \ for 'MPLABC bugs? - !C, senor!' in subject line. ()
Make payments with PayPal - it's fast, free and secure! Help us get a faster server
FAQ page: www.piclist.com/techref/microchip/languages.htm?key=mplab
Search entire site for: 'MPLABC bugs? - !C, senor!'.

Truncated match.
PICList Thread
'MPLABC bugs? - !C, senor!'
1997\02\13@211822 by Steve Hardy

flavicon
face
{Quote hidden}

...all of which ties in nicely with my usual recommendation to stick
with assembler for small microcontrollers - especially the ones with
less RAM than a BULL paddock.

Tell me, what is the point of interposing another layer of inscrutable
software between you and the machine?  The PIC has a wonderful RISC
flavour; you can learn all its instructions in a few hours.  Better
to spend time doing this rather than bashing your head against a wall
trying to figure out what the compiler has done to your nice algorithm.

Also, tell me what is the point of using a language which is so nobbled
by limitations of the machine?  C without a stack, heap, float, qsort()
and int ((**)(void ***)) pointers is like (to quote a well known
Aussie song) a pub with no beer.

I say this in spite of having written over 100,000 lines of C (and
loving it).  However, horses for courses and the horse for the PIC
course is Microchip Assembler, of course -- no offence to all the
compiler developers out there (you're doing a great job fellas,
don't give up on it just yet).  Naturally, the assembler has to be
the dinkum one - not the yucky Intel lookalike or whatever it is.

Regards,
SJH
Canberra, Australia

1997\02\14@011649 by John Payson

picon face
> ...all of which ties in nicely with my usual recommendation to stick
> with assembler for small microcontrollers - especially the ones with
> less RAM than a BULL paddock.

For small projects, there is probably no compiler that can do as well as a
good programmer at squeezing out every last byte of RAM, instruction, or
CPU cycle; this is true especially of the last point because compilers have
no way of knowing what parts of the code are time-critical (no wasted cycles
at all allowed), what parts need to run quickly (but need not be hyper-
optimized) and what parts could run 10 times slower than optimal with no
consequence to the end user.

On the other hand, even though a hand-optimized piece of code may be better
at storage allocation than a compiled C program, the latter may be much more
adaptable.  For example, consider the case in both assembly and C where you
write a routine that needs two f-registers for internal computation; the
values in these registers when the routine is called will be lost.  In ass-
embly language, you may produce two variables with names unique to that
routine, but unless you are willing to waste those variables any time they
are not in use you will have to identify other variables that will never be
"live" simultaneously and overlay them.  Once you do this, you then have to
ensure that you never accidentally change the program such that both var-
iables are live simultaneously.

For example, you might have a routine which shifts a byte out to a display
latch and a routine which sends a byte to the RS-232 port.  While initially
those routines may never be live simultaneously, someone who decided that
the display would look better if it continued to be updated during RS-232
transmission might modify the "system delay" procedure to call the display
latch routine.  Such a call could cause severe problems, however, because
the display routine would trash the outgoing message byte.  While it is
certainly possible to code around such problems, maintaing code which is
written in that fashion can be a severe pain.

In C, on the other hand, you could simply code something like:

void out_shifter(unsigned char ch)
{
 unsigned char i;

 i=8;
 do
 {
   datapin=ch.7; clockpin=1; ch<<=1; clockpin=0;
 } while (--i);
}

void sysdelay()
{
 while (not_done)    /* Assume not_done is set by an interrupt or RTCC
                        overflow or something... */
   ;
}

void out_serial(unsigned char ch)
{
 unsigned char i;
 out232 = 0;
 i=8;
 do
 {
   sysdelay();
   out232=ch.0; ch>>=1;
 } while (--i);
 sysdelay();
 out232 = 1;
}

and a decent C compiler would notice that out_serial and out_shifter were
never live simultaneously and thus overlay their variables for you.  Unlike
hand-overlaying those variables, however, letting C overlay them would ensure
that if, e.g., the sysdelay() routine were changed to call out_shifter() from
within the while loop the overlayed variables would be split.  Of course, this
may cause your program to run out of memory but it's far better to have the
compiler squawk than to have an assembler program which crashes because a
register gets changed unexpectedly.

> Tell me, what is the point of interposing another layer of inscrutable
> software between you and the machine?  The PIC has a wonderful RISC
> flavour; you can learn all its instructions in a few hours.  Better
> to spend time doing this rather than bashing your head against a wall
> trying to figure out what the compiler has done to your nice algorithm.

C compilers for the PIC are improving.  They're not "there" yet, necessarily,
but a good C compiler can do many things about as well as a decent programmer,
and often in a way which is much easier to read (this is especially true for
moderately complex math.  In one program I wrote using the CCS compiler, I
had to compute some rather nasty interpolation functions; although I had to
write my own 16x16->top16 [rounded] multiplication routine, the resulting
code flows quite logically.  Of course, some people might cringe at the level
of maths I was cramming into the poor thing, but the particular application
(a measurement instrument) doesn't need its maths to run especially fast (the
measurement hardware is limitted to 1.5 measurements/second anyway, and the
calculation routines--written in C--take about 200ms).

If you like assembly, tell me how you would possibly code something that
reads as well as the following: [a paraphrase and simplification of my real
code]

/* The variables sensor1 and sensor2 are updated via interrupt handler, and
  are thus declared globally.  The variables base_value and display_base
  never change once they are read from eeprom, and thus are declared glob-
  ally to avoid unnecessary work moving them around.  The identifier "ui"
  is a typedef for an unsigned 16-bit integer, recip(ui X) computes within
  +/-1 the value 16777216/X, and fmult(ui X, ui Y) computes precisely the
  value (X*Y + 32768)/65536.

ui sensor1, sensor2, base_value, display_base;
ui display_value(void)
{
 ui correction_factor, display_offset;

 correction_factor = recip(sensor1 - base_value);
 if (currection_factor < 500 || correction_factor > 2500)
   return 0xFFFF; /* Sensor1 is broken */
 else
 {
   display_offset = fmult(correction_factor, sensor2);
   if (display_offset < display_base)
     return 0;
   else
   {
     display_offset -= display_base;
     if (display_offset > 9999)
       return 9999;
     else
       return display_offset;
   }
 }
}

About 25 lines of C code, presupposing the existence of fmult and recip.
Even assuming the existence of any reasonable set of library routines, I
challenge you to write the above in less than 100 lines of assembly and
to have it make any sense whatsoever to the poor soul who might have to
read it.

> Also, tell me what is the point of using a language which is so nobbled
> by limitations of the machine?  C without a stack, heap, float, qsort()
> and int ((**)(void ***)) pointers is like (to quote a well known
> Aussie song) a pub with no beer.

Well... the lack of a stack costs you nothing except variable-argument
functions and recursion.  Given that the only variable-argument functions
that people ever use are printf and scanf variations, and given that the
CCS compiler DOES support a significant subset of printf (though the string
argument must be constant if you want "%" items to be interpreted), the
lack of var-args isn't a big loss.  As for recursion, it is generally not
particularly useful for most microcontroller applications anyway especially
since micros can't meaningfully do an OS-trap when recursion overflows.

A heap can be implemented quite easily in C; on some of the larger devices
(e.g. the 16C74) such an implementation could actually be useful for some
applications.  The lack of floats is a little irksome, but fixed-point
maths are usually not too bad (though 32-bit and *24-bit* integer types
would be helpful).  As for the ludicrously-interected pointer you offer,
there's nothing in the PIC architecture that prevents the use of such
constructs though there would also seldom be any reason to use them either.

> I say this in spite of having written over 100,000 lines of C (and
> loving it).  However, horses for courses and the horse for the PIC
> course is Microchip Assembler, of course -- no offence to all the
> compiler developers out there (you're doing a great job fellas,
> don't give up on it just yet).  Naturally, the assembler has to be
> the dinkum one - not the yucky Intel lookalike or whatever it is.

On the measurement device I mentioned above, probably about 35% of the source
code is hand-written assembly, about 25% is a machine-generated include file
(containing constants and such), and the remainder is C.  Even though this
was my first real project using the CCS compiler and even though I found some
bugs in it (which the people at CCS promptly fixed) there is no way I could
have done the project in even twice the time using assembly language.  I
consider myself to be quite good at writing PIC code, but it took many tries
to produce a formula that would track the measurements accurately over the
full ambient temperature range; when each try entailed chainging a few lines
of C code this wasn't too bad, but had each try required me to rewrite the
assembly-language code to compute those formulas I would have been at it for
months [or more likely I simply would have used a more powerful--and more
expensive--chip in the device].

[btw, in an early draft of this post, I included a routine to send a byte
uart-style out a PIC port pin at a rate of one bit per three clocks as an
example of something I think could not be done at all reasonably in C [not
that the assembly version reads all that much better.  It didn't really fit
my main points, but it's sorta clever so I'll put it below]

; Routine to output a byte to a bit-bang port.  Although PORTB,0 is used
; here any bit could have been used with equal facility provided only that
; the BSF and BCF instructions operate acceptably on that port.  The out-
; put rate is one bit per three instructions and there is no clock skew.

       movwf   Data
       bcf     PORTB,0
       btfss   Data,0
        goto   Clr1
       bsf     PORTB,0
Set1:
       btfsc   Data,1
        goto   Set2
       bcf     PORTB,0
Clr2:
       btfss   Data,2
        goto   Clr3
       bsf     PORTB,0
Set3:
       btfsc   Data,3
        goto   Set4
       bcf     PORTB,0
Clr4:
       btfss   Data,4
        goto   Clr5
       bsf     PORTB,0
Set5:
       btfsc   Data,5
        goto   Set6
       bcf     PORTB,0
Clr6:
       btfss   Data,6
        goto   Clr7
       bsf     PORTB,0
Set7:
       nop
       btfss   Data,7
        bcf    PORTB,0
       goto    Ending
Clr1:
       btfss   Data,1
        goto   Clr2
       bsf     PORTB,0
Set2:
       btfsc   Data,2
        goto   Set3
       bcf     PORTB,0
Clr3:
       btfss   Data,3
        goto   Clr4
       bsf     PORTB,0
Set4:
       btfsc   Data,4
        goto   Set5
       bcf     PORTB,0
Clr5:
       btfss   Data,5
        goto   Clr6
       bsf     PORTB,0
Set6:
       btfsc   Data,6
        goto   Set7
       bcf     PORTB,0
Clr7:
       nop
       btfsc   Data,7
        bsf    PORTB,0
       goto    Ending          ; Waste the extra cycle
Ending:
       bsf     PORTB,0
       ; End of routine

1997\02\14@033157 by John Dammeyer

flavicon
face
At 01:26 PM 14/02/1997 EST, you wrote:

>...all of which ties in nicely with my usual recommendation to stick
>with assembler for small microcontrollers - especially the ones with
>less RAM than a BULL paddock.
>
>Tell me, what is the point of interposing another layer of inscrutable
>software between you and the machine?  The PIC has a wonderful RISC
>flavour; you can learn all its instructions in a few hours.  Better
>to spend time doing this rather than bashing your head against a wall
>trying to figure out what the compiler has done to your nice algorithm.

I thought so too until I looked at the code generated by the MPC compiler.
Perhaps not for the 16C54 but for the 16C74 and the 17C42...

For example:

Passing a parameter by value result and the compiler flag is set for 16 bit
integers and the code overlaps a page boundry.

int func( int * px ) {
 int x = *px;
 ...
 *px = x;
}


You cannot type the assembler code as fast as I did the C code and I know
the code is correctly generated.  Plus, no less efficient than what I'd do
in assembler - I've checked.  The real point is would you ever call an
assembler routine with a parameter passed as a value result.  Truth is
mostly no.  Not because you work with bad programming practices or don't
wnat to write modular code but because it's just too much work;  so globals
are used instead.

>
>Also, tell me what is the point of using a language which is so nobbled
>by limitations of the machine?  C without a stack, heap, float, qsort()
>and int ((**)(void ***)) pointers is like (to quote a well known
>Aussie song) a pub with no beer.

I ended up writing as if I was using a sophisticated macro assembler like
what used to be available on the Ahmdal 470s.  No parameters passed, almost,
and no complicated data structures.
>


I kept running up against the PIC architecture limitations of only 4K and
less than 256 bytes RAM.  Not because of the C language but because of the
complexity of the project.

Cheers,

Thanks for your comments.

john
Pioneers are the ones, face down in the mud,
with arrows in their backs.
Automation Artisans Inc.      Ph. 1-250-544-4950
PO Box 20002                  Fax 1-250-544-4954
Sidney, BC CANADA V8L 5C9

1997\02\14@092419 by D. R. Chicotel

flavicon
face
>
>For small projects, there is probably no compiler that can do as well as a
>good programmer at squeezing out every last byte of RAM, instruction, or
>CPU cycle; this is true especially of the last point because compilers have
>no way of knowing what parts of the code are time-critical (no wasted cycles
>at all allowed), what parts need to run quickly (but need not be hyper-
>optimized) and what parts could run 10 times slower than optimal with no
>consequence to the end user.
>
[snip]

>If you like assembly, tell me how you would possibly code something that
>reads as well as the following: [a paraphrase and simplification of my real
>code]
>
[snip]

>
>About 25 lines of C code, presupposing the existence of fmult and recip.
>Even assuming the existence of any reasonable set of library routines, I
>challenge you to write the above in less than 100 lines of assembly and
>to have it make any sense whatsoever to the poor soul who might have to
>read it.
>


I just want to make a quick comment about this assembler / C debate.

I think I saw an article in Circuit Cellar that solves the problem of
using C as opposed to assembler for writting easily readable and
maintainable code.

The technique involves writing the code in assembler and then placing the
C code that equates to the assembler in the comments to the right of the
assembler code.

This gives some(!) of the benefits of both languages at the same time.
Many times I see comments embedded in assembler code that look like this:

       movlw   8
       movwf   Cnt              ; Loop 8 times
loop    movf    Reg1, w          ; Move value to w
       movwf   Reg2             ; Move w to Reg2
       .
       .
       .
       decfsz  Cnt, f           ; Decrement Counter
       goto    loop             ; Is it zero yet?
       .
       .
       .

Gee!! Thanks a lot. (Duh)


I much prefer to see this:

       movlw   8                ; for (Cnt=8; Cnt>0; Cnt--)
       movwf   Cnt              ;   {
loop    movf    Reg1, w          ;     Reg2 = Reg1;
       movwf   Reg2
       .
       .
       .
       decfsz  Cnt, f
       goto    loop             ;   }
       .
       .
       .

Now you have the speed and compactness of assembler AND the readability
of C.  Using this technique, the programmer acts as the C compiler which
allows for the greatest degree of optimization possible.

Neat huh?  Just my two cents worth.  If you agree, use it - if not,
trash it.

ps - I love this list!!  I learn something most every day.

1997\02\14@095328 by Walter Banks

picon face
John Payson wrote:

> [btw, in an early draft of this post, I included a routine
> to send a byte uart-style out a PIC port pin at a rate of
> one bit per three clocks as an example of something I think
> could not be done at all reasonably in C [not that the assembly
> version reads all that much better.  It didn't really fit
> my main points, but it's sorta clever so I'll put it below]

John,

After seeing only a little challenge in the comment I recoded
the routine in C. The result isn't that much of a surprise
since C's roots are that of a structured assembler. The listing
fragment that follows generates the same code as the assembler
source you posted.

Walter Banks
http://www.bytecraft.com

                                void Payson (bits Data)
001C                                {
0005 009C    MOVWF  1C

0006 1006    BCF    PORTB,0               PORTB.0 = 0;
0007 1C1C    BTFSS  1C,0                  if(Data.0 == 0)
0008 2820    GOTO   0020h                        goto   Clr1;
0009 1406    BSF    PORTB,0               PORTB.0 = 1;
                                 Set1:
000A 189C    BTFSC  1C,1                  if(Data.1 == 1)
000B 2823    GOTO   0023h                        goto   Set2;
000C 1006    BCF    PORTB,0               PORTB.0 = 0;
                                 Clr2:
000D 1D1C    BTFSS  1C,2                  if(Data.2 == 0)
000E 2826    GOTO   0026h                        goto   Clr3;
000F 1406    BSF    PORTB,0               PORTB.0 = 1;
                                 Set3:
0010 199C    BTFSC  1C,3                  if(Data.3 == 1)
0011 2829    GOTO   0029h                        goto   Set4;
0012 1006    BCF    PORTB,0               PORTB.0 = 0;
                                 Clr4:
0013 1E1C    BTFSS  1C,4                  if(Data.4 == 0)
0014 282C    GOTO   002Ch                        goto   Clr5;
0015 1406    BSF    PORTB,0               PORTB.0 = 1;
                                 Set5:
0016 1A9C    BTFSC  1C,5                  if(Data.5 == 1)
0017 282F    GOTO   002Fh                        goto   Set6;
0018 1006    BCF    PORTB,0               PORTB.0 = 0;
                                 Clr6:
0019 1F1C    BTFSS  1C,6                  if(Data.6 == 0)
001A 2832    GOTO   0032h                        goto   Clr7;
001B 1406    BSF    PORTB,0               PORTB.0 = 1;
                                 Set7:
001C 0000    NOP                          NOP();
001D 1F9C    BTFSS  1C,7                  if(Data.7 == 0)
001E 1006    BCF    PORTB,0               PORTB.0 = 0;
001F 2836    GOTO   0036h                        goto    Ending;
                                 Clr1:
0020 1C9C    BTFSS  1C,1                  if(Data.1 == 0)
0021 280D    GOTO   000Dh                        goto   Clr2 ;
0022 1406    BSF    PORTB,0               PORTB.0 = 1;
                                 Set2:
0023 191C    BTFSC  1C,2                  if(Data.2 == 1)
0024 2810    GOTO   0010h                        goto   Set3 ;
0025 1006    BCF    PORTB,0               PORTB.0 = 0;
                                 Clr3:
0026 1D9C    BTFSS  1C,3                  if(Data.3 == 0)
0027 2813    GOTO   0013h                        goto   Clr4 ;
0028 1406    BSF    PORTB,0               PORTB.0 = 1;
                                 Set4:
0029 1A1C    BTFSC  1C,4                  if(Data.4 == 1)
002A 2816    GOTO   0016h                        goto   Set5 ;
002B 1006    BCF    PORTB,0               PORTB.0 = 0;
                                 Clr5:
002C 1E9C    BTFSS  1C,5                  if(Data.5 == 0)
002D 2819    GOTO   0019h                        goto   Clr6 ;
002E 1406    BSF    PORTB,0               PORTB.0 = 1;
                                 Set6:
002F 1B1C    BTFSC  1C,6                  if(Data.6 == 1)
0030 281C    GOTO   001Ch                        goto   Set7 ;
0031 1006    BCF    PORTB,0               PORTB.0 = 0;
                                 Clr7:
0032 0000    NOP                          NOP();
0033 1B9C    BTFSC  1C,7                  if(Data.7 == 1)
0034 1406    BSF    PORTB,0                      PORTB.0 = 1;
0035 2836    GOTO   0036h                 goto    Ending          //
Waste the extra cycle
                                 Ending:
0036 1406    BSF    PORTB,0               PORTB.0 = 1;
                                         // End of routine
0037 0008    RETURN                 }

1997\02\14@100349 by Walter Banks

picon face
>
>For small projects, there is probably no compiler that can do as well as a
>good programmer at squeezing out every last byte of RAM, instruction, or
>CPU cycle; this is true especially of the last point because compilers have
>no way of knowing what parts of the code are time-critical (no wasted cycles
>at all allowed), what parts need to run quickly (but need not be hyper-
>optimized) and what parts could run 10 times slower than optimal with no
>consequence to the end user.


I disagree in part with the comment. C compilers like assemblers
take direction from the author. Code can be written in C to
run fast but take more code space or can be recoded into loops.
I posted earlier today a high speed serial routine that was
translated from John Payson's assembler original code to C
and the generated code is the same. If the compiler just
compiles the source as presented to the best of its ability
then the application developer can have a great influence over
generated code.

The best C optimizer will not change a bubble sort into a quick
sort but it can be a very effective way to implement an
application.

Walter Banks
http://www.bytecraft.com

1997\02\14@111235 by D. R. Chicotel

flavicon
face
At 09:25 AM 2/14/97 -0500, you wrote:

>
>For small projects, there is probably no compiler that can do as well as a
>good programmer at squeezing out every last byte of RAM, instruction, or
>CPU cycle; this is true especially of the last point because compilers
have
>no way of knowing what parts of the code are time-critical (no wasted
cycles
>at all allowed), what parts need to run quickly (but need not be hyper-
>optimized) and what parts could run 10 times slower than optimal with no
>consequence to the end user.
>
[snip]

>If you like assembly, tell me how you would possibly code something that
>reads as well as the following: [a paraphrase and simplification of my
real
>code]
>
[snip]

>
>About 25 lines of C code, presupposing the existence of fmult and recip.
>Even assuming the existence of any reasonable set of library routines, I
>challenge you to write the above in less than 100 lines of assembly and
>to have it make any sense whatsoever to the poor soul who might have to
>read it.
>


I just want to make a quick comment about this assembler / C debate.

I think I saw an article in Circuit Cellar that solves the problem of
using C as opposed to assembler for writting easily readable and
maintainable code.

The technique involves writing the code in assembler and then placing
the C code that equates to the assembler in the comments to the right
of the assembler code.

This gives some(!) of the benefits of both languages at the same time.
Many times I see comments embedded in assembler code that look like
this:

       movlw   8
       movwf   Cnt              ; Loop 8 times
loop    movf    Reg1, w          ; Move value to w
       movwf   Reg2             ; Move w to Reg2
       .
       .
       .
       decfsz  Cnt, f           ; Decrement Counter
       goto    loop             ; Is it zero yet?
       .
       .
       .

Gee!! Thanks a lot. (Duh)


I much prefer to see this:

       movlw   8                ; for (Cnt=8; Cnt>0; Cnt--)
       movwf   Cnt              ;   {
loop    movf    Reg1, w          ;     Reg2 = Reg1;
       movwf   Reg2
       .
       .
       .
       decfsz  Cnt, f
       goto    loop             ;   }
       .
       .
       .

Now you have the speed and compactness of assembler AND the readability
of C.  Using this technique, the programmer acts as the C compiler which
allows for the greatest degree of optimization possible.

Neat huh?  Just my two cents worth.  If you agree, use it - if not,
trash it.

ps - I love this list!!  I learn something most every day.

More... (looser matching)
- Last day of these posts
- In 1997 , 1998 only
- Today
- New search...