Embedded

View Original

ESE101: Switching from Assembly to C Part 3

A long time ago in a blog close by, I said:

“Next time we’ll step through the C code and explain what it’s doing in detail.”

I lied. Sorry. (I also didn’t plan to have a one-plus year gap since my last post, but that’s how life goes sometimes.)

I realized that before we dive into C, I want to explain why we should use C instead of assembly. Why does your choice of programming language matter at all?

The language you use affects how you think. It affects your program’s design and implementation.

Assembly is the microcontroller’s native language. Each assembly instruction corresponds to a basic microcontroller operation. Assembly lets you set a bit to a 1 or 0, move bytes to and from registers and memory, and do some basic math. When you program in assembly, you tend to think in terms of these basic microcontroller operations.

For example, let’s look at how we write a loop to countdown from 50000 to 0 in the assembly from our first ESE101 blinky example. The assembly code stores 50000 in the register R4, decrements R4 until it reaches 0, and when it reaches 0, continues to the next instruction after the ‘JNZ’ instruction:

    MOV #50000, R4

DelayOn:

    SUB #1, R4
    CMP #0, R4
    JNZ DelayOn

In order to write and debug this assembly code you, the programmer, have to keep track of:

  1. The instructions we can use: MOV, SUB, CMP, JNZ, and the many other options

  2. The values in the MSP430 registers you are using

  3. Which operation CMP is going to compare the results of

  4. The label DelayOn

That’s a lot of context to keep track of, most of which has nothing to do with the problem you’re trying to solve.

Here’s the same thing in C:

volatile unsigned int countdown = 50000;

while (countdown != 0) {
   countdown--;
}

Since you may not be familiar with C this is called a “while loop.” The code checks is the variable countdown is equal to zero, and then decrements countdown until it’s zero.

In order to write this and debug this C code you, the programmer, have to keep track of:

  1. The C programming language.

  2. The value in the variable countdown.

That’s it. That’s much less context than the assembly code. You can write, debug, and understand that C code faster than the assembly, and there’s a smaller chance of bugs in the C since you’re managing fewer details and have fewer chances to make a mistake.

Of course if you’re familiar with assembly and C is new to you, then the assembly will look easier, but trust me: once you know C you’ll rarely wish you were writing assembly.

An English Example

If this seems too confusing, let me give you a silly, but illustrative, example.

Let’s invent a new, simple language called Basic English. Basic English is like the regular English language but it only has a few basic words. For instance, you can say:

"I hungry. Want food."

Compare that with regular English where you can say:

"I’m starving! I’d love a good burger, or maybe a big salad. Or both."

Both Basic English and regular English let you communicate well enough to stay alive, but consider what it would like to communicate with Basic English’s limited vocabulary. You couldn’t easily describe a juicy burger or a tangy vinaigrette salad dressing. This is kind of like the XKCD creator’s Thing Explainer book where Randall Monroe explains rockets with the 1000 most common English words (like our Basic English). He succeeds in describing complex things but at a high cost in terms of clarity and readability.

Human languages like English aren’t quite like programming languages like assembly or C, but hopefully you get my point: A more expressive language like regular English lets us think and communicate more easily than a simpler language like Basic English. Both get the job done, but everyone is happier using regular English.

Back To The Code

To end this post, let’s compare my thought process while writing the assembly and C and see which allowed me to think at a higher, more abstract, and more expressive level.

My thought process when writing the assembly code was: I need a countdown. There’s no ‘countdown to zero’ instruction, and there’s no ‘countdown’ register, so I need to figure out how to use the assembly instructions, register, and memory to get the job done.

When I wrote the C code above, I started with the same thought as when I wrote the assembly code: “I want to add a countdown.” Because I was using C instead of assembly, I didn’t have to consider registers, machine instructions, or memory locations; instead I was able to think, “I need a countdown, I can put that in a variable, and I can just decrement the count until it’s 0.” The C code flows directly from the thing I wanted the code to do.

Assembly forces us to think in terms of registers, memory, and microcontroller instructions. C allows us to think more about the problem we’re trying to solve and less about the low-level details of how a microcontroller works. C lets us think at a higher level of abstraction than assembly.


This post is part of a series. Check out the complete Embedded Software Engineering 101 series here.