Does anyone write assembly language?
The simple answer to my question is: yes, of course. There are things that can be done in assembly that cannot be written in C/C++, so, typically, parts, at least, of drivers and start-up/initialization code are written in this arcane way. It is, therefore, essential that an embedded software development team include one or more engineers who can write assembly code. The real question is whether there are other contexts in which assembly is needed …
Many “old hands” in the embedded software world [like me!] are quite comfortable with assembly language and quite enjoy the feeling that the operation of the code is so precisely expressed. Assembly language can be written clearly and well commented, making it easier to understand and maintain. This, however, is not an argument for using it unnecessarily. High level languages – like C and C++ – and undoubtedly more productive; more functionality can be coded in a shorter time. There are engineers who will argue that assembly language is always faster and smaller. Although I would agree that this can be the case, I would argue that frequently a compiler will do a better job than even a skilled assembly programmer. Here are a couple of examples:
Consider this code:
unsigned char arr[4]; for (i=0; i<4; i++) arr[i] = 0;
This looks like a loop, but any reasonable compiler will generate code that does a 32-bit clear, which is much more efficient. For an array size of 5, you would get a 32-bit and an 8-bit clear. For size 6, you get a 32-bit and a 16-bit. And so on, until an actual loop is the most efficient.
Although an assembly programmer could do this, they are much more likely to write a loop straight away so that changing the code, if the array size changes, would be easy. They would not want to do a complete re-write each time. A compiler does not care.
Another example is switch statements, which are a very useful feature of the C language, enabling very clear code to be written. They can take various forms, each of which may generate different kinds of code:
The case values may be few and quite random:
switch (sv) { case 1: ... break; case 27: ... break; case 9: ... break; };
This logically compiles to some if…then…else logic.
The values may be contiguous:
switch (sv) { case 3: ... break; case 4: ... break; case 5: ... break; };
This would lead to a simple address table.
The values may be almost contiguous:
switch (sv) { case 3: ... break; case 4: ... break; case 6: ... break; };
This would lead to a simple address table with dummy entries.
The case values may be many and quite random:
switch (sv) { case 1: ... break; case 27: ... break; case 9: ... break; case 11: ... break; case 99: ... break; case 98: ... break; };
The best code for this would be a look-up table.
Once again, the human programmer would shy away from having to re-write the code, if the requirements changed. They might well code a switch construct with a look-up table, which is easily maintainable and extensible, but not necessarily of optimum efficiency.