Is anyone still using assembly language? You betcha (2007)
eetimes.com
eetimes.com
I've been working with him for the past few years to modernise his products and give them a future beyond his retirement, which is finally happening now (he's 74).
The device we've been working is an agricultural data monitor that reads from sensors in the soil and atmosphere (measuring impedance, voltage, pulse and digital signals), stores it in local flash memory then periodically uploads it via to my web app via a U-Blox cellular/IoT modem.
It's amazing what he's been able to make it do, and how simple/cheap the componentry is and how low the power consumption is. And whilst the coding and debugging is pretty slow and laborious, once it works, it's incredibly reliable and stable.
Now he's retiring, if the business is to continue we'll basically have to find someone who can migrate it to higher level platforms and languages. There aren't many (any?) people around willing/able to work with this kind of assembly code these days.
It feels like a shame. Other players doing this kind of work use higher-level platforms, which may be faster to develop for and debug but are more costly to manufacture, use more power and are less reliable.
I'm hoping we can find a balance between the easier/faster development cycle of newer platforms, whilst retaining at least some of the simplicity and elegance of my father's approach.
The cell modem he is using already has a 32 arm cpu maybe even multi-core.
32bit it the new 8bit.
C is the new assembly for 99% of code. You can drop to assembly for the 1% where it matter.
not tied to the processor architecture and thus can switch to a better/cheaper cpu easily.
The core processor arch yes, but for many (most?) of these small embedded systems, a large part of the code is basically device drivers used to communicate with GPIO, a plethera of serial buses, and any number of other devices which vary from microcontroller to microcontroller. So unless you offload all that to a RTOS of some kind (which your then limited to devices the RTOS supports) switching devices isn't easy. And for many things, the RTOS overhead will be the largest consumer of CPU+RAM. That is sorta the beauty of the ardunio ecosystem were much of this has been abstracted into common libraries across a number of device families.I did a embedded project on 8051 like that, we had the legacy code 100% in asm and we moved to C. In the end I think we had about 10-20 lines of asm and everything else in C.
You are right switching cpu and thus all the peripheral is major headache, not something to take lightly.
In fact I find that learning new high level packages much harder.
With C, you just need to rewrite the device drivers (because your business logic is portable and unit tested, right?!). In assembly, it's time to throw it all away and start again.
For a trivial programme like read the ADC and do something like alert on a UART, that's not so bad. But some products are a lot more complex than that.
The bonus being able to continue using well tuned, mature routines.
An emulation done once, would bring the whole works onto a new CPU.
There's a careful value judgement to be made there. For example, if you have many products all in some dying 8-bit architecture, and it's easier to write the emulator once and then all the products are rescued, then good. If you spend 6 months writing the emulator to save yourself from 6 weeks of porting one simple program, less good.
This is when "hey wouldn't it be really cool if" meets the cold realities of commercial engineering.
I took this particular case to mean there are lots of products.
Could also just put the CPU into an FPGA, which would be a choice I would look hard at personally. Many 8 bitters are out there, some cycle exact.
Then one has a hardware solution to build. Could go quicker.
I personally would emulate.
Over all, the take away is portability has more flex in it than is often recognized.
Or it might not be. If it's just a simple loop, probably fine. If you have to move a custom assembly RTOS and an entire stack of business logic, maybe it's going to be very painful, project-risky and error-prone. Whereas if it was C, you just need to port drivers (which may already be provided by the manufacturer) and maybe do the RTOS port (if not already done). That could be days to weeks of work. A rewrite in a new assembly language could be months, at the end of which, to a customer, you have the exact same product you had before, except without a proven track record for any single byte of code.
Like everything, it depends on the project.
This year especially, it's going to be hard enough to stay alive by just shipping anything at all while competitors go to the wall under the double whammy of customer hesitancy and component shortages that can cut an established product of at the knees. And the last thing a company needs then is a long and expensive rewrite blocking shipping.
Not only that, if the chips you rewrote for end up on 100 week lead times (and thats not even the longest I've seen this month), you're completely dead in the water.
I'd be interested in knowing how they did that as it was all x86..!
Is my assumption right that the reliability of these programs is based on the proficiency and expertise of your father, rather than being a platform/language issue? Or do these platforms come with complexity that makes them more fragile in your opinion?
So yeah, it's more about the newer platforms having more hardware and software abstraction layers, so more things that can break and they have much higher power consumption.
In our applications, the weakest links are always the more "modern" communications modules, of which we've used Bluetooth and Cellular/IoT. In both cases, they are high-level SoC devices, with all kinds of capabilities and the ability to run BASIC or Python scripts on them. But they turn out to be much less reliable than our assembly code on the basic micros we use, and are the source of almost all device faults.
To illustrate the real-world advantage of the way we do it: our biggest competitor (a significant player that has shipped thousands of devices globally), uses a more modern/high-level platform (I don't know what exactly - it may be Arduino). One of our customers was given one to try out. Like most of our customers, they wanted data uploaded to the web app every 60 or even 30 minutes in order to have precise visibility of their soil moisture throughout the day. Ours can do that, running on just two 3.7V Li-ion cells that last for 1-2 years per charge. For the competitor product, to upload more than twice a day, it needs a solar panel, which needs to be mounted high up on a post above the crop. But that's a deal-breaker for this grower and many others, due to the use of pivot sprinklers and machinery.
So you are down to C knowledge, a datasheet and a register and pin map, which many people in their 20s definitely knew how to handle 10 years ago, when I used to work for Freescale/NXP. To expedite that further you can go with Processor Expert, which will generate C code and abstract much of the datasheet swordsmanship. Can be finicky to setup, but the "drivers" you get (C routines) benefit from long debugging sessions and lessons we learned about the "proper" way to use a peripheral, sometimes using info from the hardware teams that was not in any datasheet.
The thing to win with newer, power hungry platforms is the so called "race to idle". They are so powerful and have such efficient low power modes that the best battery life is obtained by cramming as much work in a wakeup as possible and then going to sleep, as opposed to having an exceptionally low power machine that is awake most of the time. So someone with an 80s paradigm might need to adjust to this reality.
Thousands of units is nothing and definitely not enough to (profitably) stay on hand tuned assembly.
I think you can definitely hire someone for training and go from there. I don't see a huge problem about assembly language.
Does your father keep notes? Is it possible to share them?
You should ask around.
Assembly language itself is not hard. At least not on those smaller 8 bit Moto chips.
What needs to happen is your father's notes, hopefully code comments, math, etc. and how that maps to the problem space needs to be recorded.
Others can write the code. Frankly, you can likely do that given a little time.
8 bit assembly is both fun and a niche skill. Tons of people write it for fun these days, myself included.
There is probably someone near you who knows this stuff from the retro computing scene.
Today there is no point. Optimizing compilers have gotten very good, and while it is still possible for hand tuned assembly to beat compiled code, with compiled code you change a flag and your code is optimized better for some other CPU that the hand tuned code for a different but compatible CPU. Then change a flag again and your code runs on a completely unrelated CPU family.
It wasn't bad actually. We had macros to do 16 bit work with 8 bit registers, etc. so the code was much more high level than it sounds. We did have to be very careful with the stack (making sure it's balanced), but otherwise very similar to coding in C.
With modern architectures, I much prefer C and intrinsics and let the compiler deal with it (and nudge it when it gets it wrong).
(Original Spacewar was PDP-11 assembly. Original Colossal Cave appears to have been FORTRAN.)
Was there others to work on the code? Was portability a goal? Would it matter if the interface is a C for DirectX anyways?
But of course you are right, good and bad code has been written in every language. And guess what folks, your code is NOT “self-documenting,” you’re just being lazy by not writing comments! OK, soapbox ended! :-)
Yes indeed. There seems to be no limit to how bad code can be written in any programming language :)
His site's still up!
> What language was RollerCoaster Tycoon programmed in?
> It's 99% written in x86 assembler/machine code (yes, really!), with a small amount of C code used to interface to MS Windows and DirectX.
Then, sometimes you write specific C idioms that you know LLVM is going to turn into certain instructions.
For highest performance, you will always think in assembly, and in fact in the internals of your target CPU (as far as they are known), even if you have various layers of software to produce the bitstream in your object files.
It's because the systems are underpowered enough that using a higher level language wouldn't get very good results, and because access to the original source code is basically nonexistent. For those titles which have been entirely disassembled (like say, Super Mario 64), the average mod now seems to be made in a language like C instead.
Also on a machine code level you have to deal with everything yourself. How to represent floating point math? Fast division? Large datasets? Clipping to deal with 3d vertices?
Blitter settings sometimes feel like magic not science.
Small projects are indeed fun, however larger one in my opinion not really.
The stuff you will need most is in Volume 3.
nasm: x86/x86_64 intel syntax used by ffmpeg. Main issue: its SDK is horrible with disgusting code generators and so on. It has also a powerfull macro processor, then carefull not to abuse it.
gas: from GNU binutils, tons of ISAs, but for x86/x86_64 you should switch it from its default, AT&T syntax, to intel syntax.
If some definitions have to be shared with C, I would recommend to combine their usage with a C preprocessor (gas is made to be friendly to a C pre-processor).
I did cheat since I started directly with fasmg. Nowadays, I use 2 assemblers: fasmg and gas (I am only a user of nasm via ffmpeg building, and I _really_ dislike nasm SDK).
Sometimes, when you do the performance profiling of your software, you can find bottlenecks for which your programming language could be the issue. I'm particularly a fan of the ctypes Python library, libffi and similar. Android NDK is a brilliant piece of SDK to get closer to the metal in terms of performance.
Assembly language is not old; it just evolves with micro-architecture.
Timely?
Of course, generally, lots of crypto libraries have ASM, but the TLS example is interesting because it's everywhere.
For reference, STM32 series, launched in 2007, were one of the earliest Cortex-M MCUs.
There was one alternative to assembler which saw some use (albeit never reached assembler's popularity) – SabreTalk (aka PL/TPF) [0]. That was jointly developed by IBM, American Airlines and Eastern Airlines. It was a custom PL/I dialect, rather than just standard PL/I, because (i) standard PL/I includes a lot of facilities which didn't make sense in the TPF environment (such as IO and floating point); (ii) standard PL/I lacked inline assembly, but that made it harder to invoke the TPF API which revolved around assembler macros. IBM has a long history of secret internal dialects of PL/I, which it used to write its mainframe/midrange operating systems (PL/S and its descendants)–SabreTalk belongs to the same tradition, although it is not identical to any of those internal IBM dialects. Eventually, someone developed a SabreTalk to C converter, and SabreTalk sites used that to move to a mainstream language.
Most of these airline reservation systems also used COBOL. COBOL was too inefficient for the real-time transaction processing part of the system, but these systems also involved some background batch processing (aggregate reporting, accounting, etc), and that part of the system was generally written in COBOL running under MVS. It was normal for TPF sites to have TPF mainframes to run the transaction processing, and MVS mainframes to run the batch reporting and other business applications, and also to host the development environment – TPF has never been self-hosting, developers would edit/assemble/compile their code under MVS and then transfer it to TPF for testing. (In TPF's contemporary successor z/TPF, the development platform has shifted from MVS to Linux.)
[0] See https://en.wikipedia.org/wiki/SabreTalk and also http://teampli.net/Sabretalk_Reference_Guide.pdf
I feel the desire to actually learn it again and build bigger stuff.
I mean, let's be real: a needle and a steady hand hasn't been the key to getting the little man in the box to do your bidding for a while!
and that alone...
Last year, someone was updating this code to include ARM and some newer SIMD instruction sets. We discovered that in that time period, gcc has gotten to the point where even our hand-crafted asm is barely any better (and in some cases worse) than gcc's own generated code.
Compilers improve!
Hand optimized assembly doesn't necessarily age well, because a large part of the skillset is conforming the code to the processor micro-architecture. Compilers do a lightweight version of this with the -mtune where they utilize tables reflecting the number and type of functional units, instruction latency and throughput, etc to make various tradeoffs in instruction selection and/or placement. The usual "simple" example is a memcpy operation. There are about a dozen different ways (int register moves, utilizing vectors of various sizes, nontemporal, rep movXX, etc) to implement it, and depending on processor, memory subsystem, alignment, and transfer size there can be significant perf diffrences when picking one alternative over another even before considering whether to try prefetch hints, strip mining, or any of the other things that can made a difference.
Bottom line, just like playing with differing compilers, and compiler options can easily provide a 2-5x uplift its likely that an expert assembly programmer can gain that much or sometimes significantly more (because they can fundamentally change the algorithm) against a compilers best output, there remain a fair number of things that can be done with hand tuning that compilers simply aren't yet capable of. Since your using SIMD the compiler could be beating it simply because its utilizing one of the wider vector instruction sets vs the original code. Although, for a software package where you don't know the final machine much of this becomes more difficult unless your willing to have a half dozen optimization targets selected at runtime.
Bottom, line if it matters that much, there are still people who can take the best code you get out of a C/C++ compiler and generally provide an uplift. Whether its worth the effort, that is another question.
- The compiler had to make conservative assumptions. Less true now you have things like restrict pointers
- The compiler didn't know how to do SIMD. Now it can with intrinsics.
- The compiler didn't know how to do the necessary transforms to make use of in-order cores. out-of-order makes the cpu do this itself, and is making its way down to smaller processors.
When you can actually plug in and see what you get out to vet for correctness, I'd think there shouldn't be as much need to per se ignore the compiler.
This is not perfect, but in all cases, it is much less worse than to depend on compilers like gcc/clang.
I absolutely love AVR8 assembly but the AVR8 is a dead end in terms of performance (other than maybe "build a soft AVR8 inside an FPGA and delegate tasks to the gate away")
The fact that I can recompile C to ARM keeps we writing code in C even though I hate it.
Granted it is not a modern-style piece of software, but look at David Murray's Attack of the PETSCII Robots: originally hand-written in 6502 assembly for the Commodore PET and since ported to something like 20 different platforms, many of which have wildly different video and audio hardware and several of which use a completely different processor.
Microsoft wrote BASIC and others significant software in such a way.
https://devblogs.microsoft.com/commandline/microsoft-open-so...
Also compilers are hungry, wasteful, complex piece of codes fairly unsuited for 8 bits and 16 bits machines. DOS games started being written in C only when the 386 got commonplace.
Heck, even on 32 bits machines of the times (VAX), most things were written in pure assembly. That includes VMS.
When TP4 and Turbo C were introduced with their full TUI environment, the 8088 started to struggle.
To be fluent in Assembler you must understand the hardware in detail.
However once you move to a high level language, the hardware becomes relatively invisible.
I started out with calculator chips in the '70s, then moved to the 6502, the 8051, and then to the PIC series.
I absolutely love the larger PICs.
But then the Arduino came along and suddenly the hardware became invisible.
For that reason I've always disliked programming in C, python, etc, and absolutely detest C++
I reckon that many millenial programmers tend to avoid Assembler because they simply don't understand the hardware.
This was not that long ago, around 15 years ago. I really enjoyed AVR assembly. Are these microcontrollers still in use nowadays?
I feel like we've gone full circle.
It was trivial to implement threads on that machine, just execute a BLWP (branch and load workspace pointer) when jumping between threads.
I mostly write mobile app codes (Java/Kotlin), and there's no way I'll use assembly for work. But for fun? Heh why not? :)
I'd claw my eyes out if I had to write entire applications in assembly.
I am shocked.