Packing bits in XC8

nsaspook

Joined Aug 27, 2009
16,328
Actually, my commercial .asm code is laid out pretty damn well, in a very organized fashion, and is pretty easy to maintain.
Of that I'm sure but it you is very organized asm code to you as the developer and long time maintainer of your personal abstract machine language model.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,300
For the first time in my professional life, I decided to peruse Microchip's reference code to get a feel for the hoops one must jump through to program a PIC in C -- at a level a bit higher than just blinking an LED. I assume the Microchip code developers are uber programmers, so they must write the best code possible, no?

Many of my products include multiplexed custom LCDs, using 18F chips like 18F8xJ90's and 18F65K90's. Microchip has a development board for the 18F86K/J90 parts.

Looking at their code, I found this construct -- repeated over and over -- for painting the LCD:

C:
void S1Num (unsigned char num)
{
    switch (num)
    {
        case 0: S1AON; S1BON; S1CON; S1DON; S1EON; S1FON; S1GOFF;break;
        case 1: S1AOFF;S1BON; S1CON; S1DOFF;S1EOFF;S1FOFF;S1GOFF;break;
        case 2: S1AON; S1BON; S1COFF;S1DON; S1EON; S1FOFF;S1GON; break;
        case 3: S1AON; S1BON; S1CON; S1DON;    S1EOFF;S1FOFF;S1GON; break;
        case 4: S1AOFF;S1BON; S1CON; S1DOFF;S1EOFF;S1FON; S1GON; break;
        case 5: S1AON; S1BOFF;S1CON; S1DON; S1EOFF;S1FON; S1GON; break;
        case 6: S1AON; S1BOFF;S1CON; S1DON; S1EON; S1FON; S1GON; break;
        case 7: S1AON; S1BON; S1CON; S1DOFF;S1EOFF;S1FOFF;S1GOFF;break;
        case 8: S1AON; S1BON; S1CON; S1DON; S1EON; S1FON; S1GON; break;
        case 9: S1AON; S1BON; S1CON; S1DON; S1EOFF;S1FON; S1GON; break;
        case A: S1AON; S1BON; S1CON; S1DOFF;S1EON; S1FON; S1GON; break;
        case C: S1AON; S1BOFF;S1COFF;S1DON; S1EON; S1FON; S1GOFF;break;
        case E: S1AON; S1BOFF;S1COFF;S1DON; S1EON; S1FON; S1GON; break;
        case F: S1AON; S1BOFF;S1COFF;S1DOFF;S1EON; S1FON; S1GON; break;
        case H: S1AOFF;S1BON; S1CON; S1DOFF;S1EON; S1FON; S1GON; break;
        case L: S1AOFF;S1BOFF;S1COFF;S1DON; S1EON; S1FON; S1GOFF;break;
        case P: S1AON; S1BON; S1COFF;S1DOFF;S1EON; S1FON; S1GON; break;

        case No_Disp: S1NumOFF;
    }
}
This is for painting a single 7 segment digit on the panel, and requires a minimum (best I can tell) of 137 code words plus overhead. This is repeated 8 times (once for each digit of the display) for a total of at least 1,096 instructions. And this doesn't include the various annunciators.

As a point of reference, eons ago (circa 1992) I was writing similar apps in .asm on a PIC16C54 using less than 512 instructions for the entire app!

And for comparison, one of my current products -- using a PIC18F84J90 -- my entire LCD paint routine (performed during a "waveform B" LCD interrupt) uses a total of 34 instructions and 44 ROM words.

Seriously???

Maybe you expert C guys can afford a chip with 32 times the ROM required for what I can do in .asm, but I can't.

Just based on this, I see that I have no choice but to continue in .asm -- albeit struggling with MPLabX if I want to use the newer parts.

Funny, I struggle with this decision every couple of years, and I always come to the same conclusion:

For small embedded processors, C sucks.
 
Last edited:

nsaspook

Joined Aug 27, 2009
16,328
Seriously???

Maybe you expert C guys can afford a chip with 32 times the ROM required for what I can do in .asm, but I can't.

Just based on this, I see that I have no choice but to continue in .asm -- albeit struggling with MPLabX if I want to use the newer parts.

Funny, I struggle with this decision every couple of years, and I always come to the same conclusion:

For small embedded processors, C sucks.
Seriously??? If this example is part of your rational for not using C it's pretty weak soup. To be a good embedded C programmer you need to have mastered at least one Assembly language to think like a machine when you write C code because understanding the machine-language programming model is important in small devices where code size is a critical factor. It's a programming balancing act to keep good C modular structure vs 'clever' machine-language programming model speedup's and code reductions.

https://barrgroup.com/Embedded-Systems/How-To/Assembly-vs-C-Comparison
[1] In fact, they'd wanted it written in C in the first place. But the engineer they'd hired decided on assembly, apparently because C scared him. He convinced them to go along by saying vague things like "C is too big" and "C is too slow."

[2] To be fair, I did benefit from the work of the original programmer. In certain instances, I was able to borrow the algorithm for an essential peripheral interaction from his code, rather than reconstruct the sequence from a databook. If I hadn't had access to these working parts of the code, I probably would have spent about 100 more hours on the project (50 of them staring at a logic analyzer). [back]

[3] The speedup was actually the result of a better design. Assembly will always be at least as fast as C when executing the same algorithm.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,300
Seriously??? If this example is part of your rational for not using C it's pretty weak soup.

https://barrgroup.com/Embedded-Systems/How-To/Assembly-vs-C-Comparison
Here's my analysis (for the types of embedded apps I write):

Efficiency of compiled code: .asm hands down.
Source code portability: .asm == C for PIC (i.e. nearly none at all). C is worse if embedded .asm is required.
Program maintainability: Depends on programmer.
Typical bug rates (say, per thousand lines of code): .asm: no hidden bugs. C depends on libraries and optimization level (i.e. hidden bugs).
The amount of time it will take to develop the solution: .asm much faster than C (for me and for various reasons)
The availability and cost of the compilers and other development tools: .asm free. C free for zero optimizations.
Your personal experience (or that of the developers on your team) with specific languages or tools: .asm hands down.

Looks like .asm is the choice for me. Like I said much earlier, a lot depends on one's religion.

Rewriting the code in C (including design and performance improvements): 185 hours
Nearly 5 weeks for the equivalent of a mere "2000 lines" of .asm code. This guy is slow as molasses. I'd need 2 weeks max to rewrite and test the whole thing.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,300
I should again point out that the only reason I am going through this exercise is the limited .asm support in MPLabX.

And the only thing it is really missing, from my point of view, is the ability, in the debugger window, to a) use arbitrary symbolic labels as register addresses and cast the data at those addresses into a type of my choosing.

If it could do that, I'd have no problem using X for .asm and I could continue along my happy way forever ignorant of the ways of embedded C.
 
I have, basically, only used MPASM for programming PICs and only the smaller ones. I have used XC8 a few times but now I find myself needing to learn to use it because I have purchased a couple of demo boards with larger PICS including a 32-Bit.

So, that's why I read through this thread. It seems to me joeyd, that you already made up your mind that real men don't use C and, you will simply stay with your horse even when a new-fangled automobile might offer some advantage.

I say that you can do what you want to do with XC8 and you can do it easily. I say this so that you will actually test the methodology and report back, thus saving me the trouble of doing the work in order to find out the answer.

You need to use the BIT data type (5.4.2.1). This is, of course, not standard C and would not be transportable. It is, instead optimized for the PIC by the PIC and is of the PIC.

Your MPASM statements like:
#define pwrup flag0,0,0 ;0=normal/1=powering up
#define minboot flag0,1,0 ;0=normal/1=minboot

simply become:

BIT pwrup
BIT minboot

Now I bet you are saying bfd because the crux of the biscuit is whether or not an entire byte is allocated for each BIT variable defined. I know your thinking this because I know joeyd don't use no 15 byte when two byte will do - joeyd don't run like dat.

I say XC8 will optimize it just exactly the way you want, because I believe them when they say:

The bit variables behave in most respects like normal unsigned char variables, but they may only contain the values 0 and 1, and therefore provide a convenient and efficient method of storing flags. Eight bit objects are packed into each byte of memory storage, so they don’t consume large amounts of internal RAM.

Furthermore, I am thinking that you can slap one include containing the whole S-load of your flags and the compiler will only use what it needs and, at worst, will give you a bunch of warnings - but then people have been warning you all of your life :)

If you do test this out, would you please share the results?
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,300
you will simply stay with your horse even when a new-fangled automobile might offer some advantage.
I've yet to see any advantage as it pertains to me.

Your MPASM statements like:
#define pwrup flag0,0,0 ;0=normal/1=powering up
#define minboot flag0,1,0 ;0=normal/1=minboot

simply become:

BIT pwrup
BIT minboot
Very interesting. If this is true, I'm surprised it took a novice to discover this!

If you do test this out, would you please share the results?
I actually had a thread around here somewhere where I was giving a play-by-play rundown of my experience with the then-new MPLabX. I gave up when I realized I couldn't process IEEE floats through the debugger window in a .asm environment.

Your post gives me hope. When I have some time, I'll play around and report back. I am still concerned about code bloat, though, as I demonstrated clearly in the above LCD post.
 
Very interesting. If this is true, I'm surprised it took a novice to discover this!
@joeyd999 I appreciate being able to speak candidly and colorfully - I know from experience on the board that you are not thin-skinned and can take it as well as dish it out.

It is very interesting indeed. I am not insulted by either your surprise or your calling me a "novice". Fact is, I have never made my living by programming or selling embedded devices etc... so, I shall wear the novice badge you have given me with pride.

On the other hand, it may depend on how you define "novice". I probably wrote my first program, maybe 35-40 years ago. I have rarely been directly paid for programming (directly in contrast to it being part of another project related to another objective), although it has happened a few times, many years ago (e.g., see here and here under the speech attribution), but also on occasion more recently and even with a PIC (e.g., here).

But, no matter, the point I want to make on this is that your surprise may, possibly, be mis-attributed to the characteristic of being a "novice" when it might be better explained by a willingness to spend 15 min reading a manual without the blinders of prejudice. ;)
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,300
@joeyd999 I appreciate being able to speak candidly and colorfully - I know from experience on the board that you are not thin-skinned and can take it as well as dish it out.

It is very interesting indeed. I am not insulted by either your surprise or your calling me a "novice". Fact is, I have never made my living by programming or selling embedded devices etc... so, I shall wear the novice badge you have given me with pride.

On the other hand, it may depend on how you define "novice". I probably wrote my first program, maybe 35-40 years ago. I have rarely been directly paid for programming (directly in contrast to it being part of another project related to another objective), although it has happened a few times, many years ago (e.g., see here and here under the speech attribution), but also on occasion more recently and even with a PIC (e.g., here).

But, no matter, the point I want to make on this is that your surprise may, possibly, be mis-attributed to the characteristic of being a "novice" when it might be better explained by a willingness to spend 15 min reading a manual without the blinders of prejudice. ;)
I have, basically, only used MPASM for programming PICs and only the smaller ones. I have used XC8 a few times but now I find myself needing to learn to use it because I have purchased a couple of demo boards with larger PICS including a 32-Bit.
My use of "novice" was entirely correct -- as per your words -- regarding PIC and XC8.

From one novice to another, I stand by my comment, and you should take no offense.
 

nsaspook

Joined Aug 27, 2009
16,328
I've yet to see any advantage as it pertains to me.



Very interesting. If this is true, I'm surprised it took a novice to discover this!
The 'better' functional equivalent of BIT is this:

typedef struct foo
{unsigned x:1;
} foo;

How the compiler packs or pads the structure in native machine words is implementation depend.

From the XC8 user guide:
It is not possible to declare a pointer to bit types or assign the address of a bit object
to any pointer. Nor is it possible to statically initialize bit variables so they must be
assigned any non-zero starting value (i.e., 1) in the code itself. Bit objects will be
cleared on startup, unless the bit is qualified persistent.
 
Last edited:

xox

Joined Sep 8, 2017
936
I've never programmed a PIC or any other microcontroller for that matter. But I have written plenty of x86 assembler and know well the satisfaction that comes along with understanding every nook and cranny of a program that has a small memory footprint. I get it. But at some point you do have to admit that the efficiency of your development cycle is unavoidably limited by the complexity of the project.

I can write code for an Arduino in C++ and leverage several orders of magnitude more functionality than a C program that accomplishes the same thing, nevermind how much more so than its ASM equivalent. And you can burn an insanely large program onto a standalone ATmega chip to produce a solution that is just as comparable to a PIC in terms of size, cost, and power-consumption, so the argument that it simply won't work for this or that application just doesn't really hold up anymore.

If you can accept that then you should also be able to realize that this whole idea of mucking together the global variables of your driver libraries essentially just amounts to a futile exercise in over-optimization (not to mention a very ugly hack that could easily lead to mysterious bugs cropping up in your applications).

The times are a changin' Joey. Might as well roll with it...
 
My use of "novice" was entirely correct -- as per your words -- regarding PIC and XC8.

From one novice to another, I stand by my comment, and you should take no offense.
yeah, I'm probably just getting too sensitive in my old age - n/p and sorry - I better go listen to some moody blues until this passes :)
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,300
I've never programmed a PIC or any other microcontroller for that matter. But I have written plenty of x86 assembler and know well the satisfaction that comes along with understanding every nook and cranny of a program that has a small memory footprint. I get it. But at some point you do have to admit that the efficiency of your development cycle is unavoidably limited by the complexity of the project.

I can write code for an Arduino in C++ and leverage several orders of magnitude more functionality than a C program that accomplishes the same thing, nevermind how much more so than its ASM equivalent. And you can burn an insanely large program onto a standalone ATmega chip to produce a solution that is just as comparable to a PIC in terms of size, cost, and power-consumption, so the argument that it simply won't work for this or that application just doesn't really hold up anymore.

If you can accept that then you should also be able to realize that this whole idea of mucking together the global variables of your driver libraries essentially just amounts to a futile exercise in over-optimization (not to mention a very ugly hack that could easily lead to mysterious bugs cropping up in your applications).

The times are a changin' Joey. Might as well roll with it...
You're a relative newcomer here, @xox, so I'll try to give you some slack.

You don't know me, or what I do, or what my level of experience is, or how good I really am at what I do, so it's rather presumptuous of you to make statements like:

"...the efficiency of your development cycle is unavoidably limited by the complexity of the project."
"...this whole idea of mucking together the global variables of your driver libraries essentially just amounts to a futile exercise in over-optimization..."
"...mysterious bugs cropping up in your applications..."

First, I make money doing what I do. I take it seriously.
Second, my designs are complex only to the extent they need to be. A programming language isn't going to change that.
Third, my market -- where cost is king -- requires "over-optimization" in order to compete. Pennies, clock cycles, and power consumption make a huge difference.
Fourth, on the rare occasion I write bugs, they are never mysterious. And they never get shipped. I ship hardware that must work on day one. I cannot upgrade firmware in the field. It would get very expensive very quickly if I shipped buggy code.
Fifth, I've been writing .asm for 40 years continuously. Nearly 30 of those has been with PIC hardware. I was an early adopter because I saw the promise in the PIC architecture.
Six, I can write the equivalent in .asm faster than most "experts" can write in C. I've proven this in real life.
Seventh, a typical application for me generally runs about 12,000 to 15,000 lines of code, and takes me about 2 weeks to write it. A big time saver is the dozens of fully functional and tested .asm libraries I have written over the years that are essentially plug-and-play into a custom code framework that I spent years refining. I can write new apps in my sleep.
Eighth, I've never met a third-party library I liked.
Ninth, for me to "roll with it" would be a huge step back in terms of sunk costs and time in already developed code. I would need a real good reason to absorb that.

I am sure I could think of a dozen more points to make, but this should make clear the following:

1. I really, really enjoy writing good .asm code.
2. I love blowing the doors off you C guys in terms of execution speed and code-size.
3. I would need a really, really good reason to upend my business and life to do things otherwise -- and so far, nobody's given me one.
 

xox

Joined Sep 8, 2017
936
You're a relative newcomer here, @xox, so I'll try to give you some slack.

You don't know me, or what I do, or what my level of experience is, or how good I really am at what I do, so it's rather presumptuous of you to make statements like:

"...the efficiency of your development cycle is unavoidably limited by the complexity of the project."
"...this whole idea of mucking together the global variables of your driver libraries essentially just amounts to a futile exercise in over-optimization..."
"...mysterious bugs cropping up in your applications..."

First, I make money doing what I do. I take it seriously.
Second, my designs are complex only to the extent they need to be. A programming language isn't going to change that.
Third, my market -- where cost is king -- requires "over-optimization" in order to compete. Pennies, clock cycles, and power consumption make a huge difference.
Fourth, on the rare occasion I write bugs, they are never mysterious. And they never get shipped. I ship hardware that must work on day one. I cannot upgrade firmware in the field. It would get very expensive very quickly if I shipped buggy code.
Fifth, I've been writing .asm for 40 years continuously. Nearly 30 of those has been with PIC hardware. I was an early adopter because I saw the promise in the PIC architecture.
Six, I can write the equivalent in .asm faster than most "experts" can write in C. I've proven this in real life.
Seventh, a typical application for me generally runs about 12,000 to 15,000 lines of code, and takes me about 2 weeks to write it. A big time saver is the dozens of fully functional and tested .asm libraries I have written over the years that are essentially plug-and-play into a custom code framework that I spent years refining. I can write new apps in my sleep.
Eighth, I've never met a third-party library I liked.
Ninth, for me to "roll with it" would be a huge step back in terms of sunk costs and time in already developed code. I would need a real good reason to absorb that.

I am sure I could think of a dozen more points to make, but this should make clear the following:

1. I really, really enjoy writing good .asm code.
2. I love blowing the doors off you C guys in terms of execution speed and code-size.
3. I would need a really, really good reason to upend my business and life to do things otherwise -- and so far, nobody's given me one.
Look, I'm not questioning you abilities here. But I too have a lot of experience writing software. That's what I do for a living. I've worked on projects which deal with several million lines of code. All I'm saying, from one professional to another, is that it really might be in your best interest not to dismiss the power and flexibility of developing applications in a higher level language. That's all. Maybe your project is small enough that it doesn't warrant the extra overhead. Fine, that makes sense. But if that day should ever come that your customer requires an exponentially more complex application then don't be foolish. Use the best tool for the job.
 
Top