Assembly vs C

For some reason there is a lot of discussion and argumentation about C vs Assembly language when it comes to programming microcontrollers. First of all, I am really amazed that there even exists a C compiler for a device like the 16F876. I have to take my hat off to the people who designed such a compiler. They have done a fantastic job considering that there are only 35 instructions and limited memory in an ‘876. They have created a very useful tool that gives microcontroller programmers the power to create readable, and capable code.

Secondly, more and more people involved in microcontrollers are learning C and applying it in all sorts of limited hardware environments to good use. Ideas can be transmitted clearly and quickly. You can make the argument that code sharing is more effective using C.

My background in electronics includes a first programming experience writing in assembly language on a Z80. I taught myself Z80 assembly language to, first of all, understand the chip hardware functionality and capabilities, and then to develop debugging tools to diagnose system hardware problems. Later, I learned assembly for the 6805 and 68000. The first microcontroller I programmed in assembly was the Z-8. So, to me, assembly language is just a given. It really influences the way I “picture” a microcontroller in my mind.

I learned C for the first time when writing a little home-brew development system on an Amiga. I quickly realized the power of a higher level language and the ability to use libraries of useful functions. There were tons of code already written for me. I had the power of structures and jump tables that the compiler laid out for me so I did not have to worry about where they were being placed in memory. Programming had a new level of speed and efficiency.

Working on the PWM-ADC project on these pages was the first time I ever used C on a microcontroller. One thing I noticed was that the compiler handled all of the register bank selections for you. That’s pretty neat! Sometimes I still make a register bank error in assembly. That is a problem that can be difficult to find. I’m sure there are other, similar, benefits in using C that I have not discovered yet. But I always had to stop and think at a lower level and “translate up” to C in my mind. In other words, I had to un-think some of the detail of the PIC and replace that detail with an abstraction. Now, this was a little spooky to me. I was “giving up” a fine grained understanding of what was happening on the chip just to gain some convenience. I wasn’t really comfortable with the feeling.

Of course, such issues do not matter on a machine like a PC. The clock rate is much higher. There are gigabytes of memory available. The details of peripheral interfaces are handled by APIs and library calls. Those also run at high speed and with gigs of memory. But a microcontroller is fundamentally different. You are limited to lower speeds and much less memory. You don’t have room for APIs and libraries of code. And those must be written in a generalized way to provide utility for many different users, which takes up more memory. Writing a substantial piece of code for a microcontroller this way will quickly fill your memory.

Execution speed is another big issue. There is a rule of thumb you quickly learn when writing in assembler. Conserving memory costs speed, and going fast consumes memory. This is true for C also, of course. But no one ever notices because they are not thinking in terms of instruction cycles and memory consumed. There are many tricks available to the assembly coder that just do not present themselves in C. There are side effects with status register bits you can take advantage of. If you have ever reduced a logic circuit using a Karnaugh Map you know the great advantage of “don’t care” states. (You even know what a “don’t care” state is.) In assembly, you have a much greater opportunity to take advantage of these states than you do in C. These are just two examples of “tricks” that can help with execution speed and memory size.

So, “I’ll just get a chip with higher clock rate” or “I’ll just get a chip with more memory”. Yes, those are solutions to these problems. If you are a hobbyist these may be great solutions. After all, you get to gain experience on a more advanced MCU. But if you are planning to work in this field as a profession, you don’t always get to make this call.

Often, hardware and software development must occur in parallel to shorten time-to-market. The fundamentals of the hardware design are going to be locked-in at a very early stage of the development process. Software will be constrained to these early hardware choices. And these choices are many times based on features and price of your competitor. You have to do better than those guys. What happens when software development hits the end-of-memory? Are you going to tell everyone you need a new processor? One with more memory? Or do you request a chip that runs faster? Are you going to make demands that require a PCB re-spin? Or just tell the boss that such-and-such feature can’t be incorporated as has been planned?

Efficiency is the hallmark of engineering. Lowest cost, greatest functionality, best efficiency. Can C meet thsse requirements? Definitely yes, in many cases. But there will be times when the “squeeze play” comes on. It may only be necessary to re-write one C function in assembly to meet a requirement. Is that when you will become an assembly language programmer? You’ll start learning assembly for your processor when the time pressure is greatest? You’ll start learning assembler “tricks” when your job is on the line? That’s not very efficient or realistic. Assembly language programming is a set of skills that are acquired over time. Practicing it now will set you up for the future. You can be the guy that tells the boss: “I can fix this.” You can be the guy that saves the project.

Or not.

I am looking forward to using more C on microcontrollers. Considering a hex keypad driver in the near future, I can imagine how much simpler and efficient it will be to code it in C instead of assembly. So, this is curious, then. If I’m such a great proponent of assembly language, why do I imagine this? Well, even without doing any preliminary design, I can imagine how a switch statement as the first layer of decoding will make the organization of a hexpad driver easier to understand right off the bat. Perhaps even a state-machine design would be interesting to do. But doesn’t this apply to writing code for any functionality? Well, I feel that I’m OK with C in this application because it is only at “human-interaction” speed. The only timing involved should be the switch debouncing. And that will most likely be in the 10-25mS range. Also, if keys have different modes at different times, it will be much easier to follow the logic trail with separate, well defined function calls.

So, it looks like it just comes down to the best tool for the job. Assembly Vs C. Basic vs Pascal. C++ vs Java. Python, perl, ruby, haskel, smalltalk, lisp and VHDL. If you have the desire, time, and opportunity to code in different languages, you should just go for it. Experience in working with different languages can open your eyes to new ideas, methods, and strategies. You will be able to think about programming problems in various different ways. It’s always easy to think you have found the “one true way”, and settle for that. But doing so will only limit your range of experience and knowledge. Take the opportunity to learn new things whenever you can.