This week, [Al Williams] wrote a great thought piece about whether or not it was worth learning an assembly language at all anymore, and when. The comments overflowed, and we’re surprised that so many people basically agree with us: yes. Of course, it’s a Hackaday crowd, but I still didn’t expect the outpouring of love for the most primitive of languages.
Assembly language isn’t really one language, though. Every chip speaks its own dialect. Of course there are similarities: every CPU has an add function, right? But almost no CPU has just one add – there are variants with and without carry, storing and reading from working registers or RAM. And once you start talking about memory access, direct or indirect, the individual architectures of the chips demand different assembly languages.
But still, although the particular ways that CPUs do what they do can be incompatible from a strictly language perspective, they are a lot more similar in terms of the programming idioms that you’ll pick up along the way. Just as learning a set of solid algorithms will help you no matter which higher-level language you use, learning the concepts behind crafting loops and simple memory structures out of raw assembly language will serve you no matter which CPU you choose.
I have only written assembly language for a handful of CPUs, and not much of it at that, but I’ve found the microcontrollers to be the friendliest. So if you want to dip your toes in that water, pick up an AVR or an MSP430. Or maybe even the new hotness – a RISC-V. You’ll find the instruction sets small enough that you have to do most of the work yourself. And that is, after all, the point of learning an assembly language: learning to think like the silicon. If you treat it like a fun puzzle to solve, you’ll probably even enjoy the experience.
[Al]’s original question was when you should learn an assembly language: before or after a higher-level language. For 99% of our readers, I’d say the answer is right now.
I am!
That’s why I haven’t touched it in decades!
B^)
I graduated in the early 80s with a comp sci degree and went on to do a variety of mod sim programming , mostly in Fortran. I remember meeting one of my old profs and explaining how, out of all those language and database such classes, the two that changed my world view and lexicon were assembly and compilers. Understanding the plumbing and processes made so much of the mystery of higher level languages clear. It wasn’t that I coded assembler at work, but the knowledge was like x-ray vision into every algorithm I wrote.
Yep. This for sure.
Yes! For sure! Assembly Language Rules!
Exactly! Even superficial knowledge of ASM is a huge advantage.
For me, my education came from writing a dis-assembler. I’ve written several and each teaches me a few new tricks.
I teach an MSP430 lab and we run that mostly in assembly and switch to C mid-semester. The students are often baffled by the choices the compiler makes and are marveled by some of the wasteful code the compile writes. It removed loops it doesn’t think are useful and spends a fair number of instructions moving data around for no reason.
It’s rare for most people to try and jam every byte of flash full of efficient code or always run in the fewest instructions, but in those cases you still need assembly.
I’ve marveled at the insanely good code that the compiler writes. Sometimes, it’s completely unrecognizable from what I wrote in C, and it’s like solving a puzzle to deconstruct how it works.
You need a better compiler. C compilers for micros started outsmarting me (more like using tricks that we avoid if we want our code to be readable) around 1990.
Depends on the processor. I’ve never seen compiler generated code better than I could do manually. But then, I’ve luckily avoided horrors like 68k and x86.
There are many places where you could write better assembly code in theory, but it’s just too much work to keep up with changes. I can have a 50 line function in C that gets compiled into 500 lines of assembly code. When I make a single change, I get 500 lines of different assembly code. When you’re doing it by hand, do you really have the time and energy to spend hours on completely rewriting the code ?
Yeah, it’s 2023, do we really need to argue about the advantages of compilers anymore? Kids who grew up before basic and are now old and sour just don’t want to feel bad about having been left behind. It’s called getting old, it happens to all of us, get over it.
First time I noticed was on 68HC11 and a nice little compiler that ran on a Mac. I can’t recall the name.
The horrors. So tell us, greybeard, what architectures don’t give you the vapors?
The MSP430 is actually a really nice machine for writing assembler on — the instruction set is a good compromise between orthogonality and functionality, and the constant generator is genius (for people who don’t know it, the instruction set allows you to use the constants 0, 1, 2, 4, 8, 255 or 65535 anywhere at zero cost). The MSP430X variant has the pleasantly bonkers 20-bit registers, too. (I did a Fuzix port to the MSP430 once.)
I just wish they made devices with external bus interfaces to allow more memory. The biggest MSP430 device I know only has about 96kB of RAM.
I agree, the MSP430 is a good choice for starting to learn assembly language. It’s instruction set is highly orthogonal and has been often compared to the PDP-11.
There are very few instructions to learn, which makes it less daunting than other microcontrollers.
It is one of a few 16-bit microcontroller architectures, and in its day, it offered extremely low power operation.
The newer parts have up to 256kB of non-volatile FRAM such as the MSP430FR5994.
As a high school student I taught myself enough Z80 to write small programs, but it was all hand assembled and entered using a hex keypad. It was tough work, with only a hex monitor.
Because of the steep learning curve, you generally focused on one cpu and its ISA, which in the early 1980s was usually Z80 or 6502, about as similar as chalk and cheese. You either fell into one camp or the other.
Aside some BASIC with the venerable C64, Assembly on the Motorola 68000 has been my first “serious” language, even before anything higher level, and helped me a lot to understand what was going on under the hood, but I had also previous experiences building stuff with both analog and TTL+CMOS logic chips, which also made things easier. I abandoned .asm pretty soon though for C, and today if I had to choose a programming language, I’d probably choose pick one among the high level languages with the smallest footprint and best performance such as Nim, Crystal, and Zig.
Those new languages have no wide adaptation and are fads imo. Everything real is still C and I will stick with that, reinforced with ASM and GPU where needed.
I am amazed about the comments.
Assembly is a tool like any C or whatever.
You need the right tool for the job.
Many moons ago with the CDP1802
it was HEX first, you toggled the switches as needed
then Assembly with the serial interface in software
then C and
Forth.
If you want to get close to the hardware, there are not many options.
If required or not is another question.
Unless you need absolutely every clock cycle and perfect timing, there is no reason to use assembly at all. Writing complex pieces of software in it is just tedious – that’s why we invented higher level languages. From a particular perspective a high level language is nothing more than a collection of assembly subroutines that are put together by replacing keywords with them (albeit with some additional work to optimize everything). This is easy to test by ordering the compiler to save assembly intermediary. Which I actually did once for free version of XC8 to show someone, how the compiler added some unnecessary nested jumps to make it execute more slowly. IIRC, it did that for all function calls, so paid version of XC8 would offer faster execution.
Seriously, the only reason to learn assembly in modern times is to write snippets of time/performance critical code, and only when high-level language compiler can’t do that. If it is impossible for some idiotic reason to include asm code in the high level source, then I would recommend writing placeholder code, compiling and grabbing the assembly intermediary from that compiler, and then modifying relevant parts by hand before recompiling it to machine code.
A good reason to learn assembly is to be able to understand the compiler output. There are plenty of times when I need to look at the disassembly to figure out a bug.
Assembly can be quite necessary if you need to reverse engineer closed source binaries, and for various related hacking needs.
Which means that your original code is incorrect. And you don’t use debug features all the compilers and platforms provide.
Sometimes compiler generate buggy code, or do unexpected optimizations. It helps to understand what’s going on, and sometimes it’s also easier and faster than looking up documentation (which is often cumbersome and abstract).
Yes, the original code is incorrect, but looking at the assembly output can help to figure out why. For instance, I had a recent problem where I had a C function as reset handler (called by hardware reset vector) that set up the system, including the stack pointer, and then called main() as the last thing. The compiler was being clever, and first cleaned up the stack frame, and then just jumped to main(), instead of calling main() as a subroutine and then cleaning up the stack. By looking at the assembly, I understood the problem, and was able to fix my mistake, and then verify it was solved correctly.
There are a lot of other reasons to know assembly languages. If you are debugging a binary that you don’t have the source code for, you must have a reasonable grasp of assembly. If you’ve come across a compiler bug (getting rarer these days, but par for the course in the past) you’ll get to the bottom of the problem much quicker with assembly knowledge. If you’re trying to interface with an early ’90s Japanese car’s ECU, you need to know assembly for its processor. If you’re working on a security exploit you’ll get a lot further with assembly knowledge. If you are porting a tool chain to a new processor, you need knowledge of assembly.
Sure, in this day and age you can get by without any knowledge of assembly. But there are a lot of non-trivial things you can do if you know assembly language(s) than if you don’t.
Yes indeed. If you don’t know at least one processor architecture (i.e. assembly language) you don’t really know computers. In many ways it is better to talk about “processor architecture” and ask questions like “how many processor architectures are you familiar with”.
It isn’t so much “dialects” as it is understanding different ways of putting together a machine.
Sounds like gatekeeping, to me.
Yep; gatekeeping guilty as charged. I won’t hire A programmer that doesn’t know assembly. They won’t use it, but the depth of understanding it requires is essential to our profession. Schools are cranking out people that write classes, not programs
Agreed, but these are specific use cases. Most programmers don’t debug or reverse engineer unknown code, but write their own. For example I program microcontrollers, and I really don’t need to know a particular assembly language to write code in C. I don’t need to know intimate details of code execution, ALU operations, nor other minutia. I just need to know, what features the uC has, SFR names, some details about clock, interrupts, peripherals, sleep and watchdog. Frankly I don’t care how values are loaded to ALU, how operations are performed and where results end up as long as my C code is executed.
Yes, it is beneficial to know some assembly syntax and to be able to read some snippets of code. Many older programs were written in assembly by people who didn’t have C (or any other high level language) compilers, or didn’t trust them. Aside from that it’s less useful than you think. Mostly because it’s tedious. Even moderately simple functions in C turn into pages and pages of ASM. Add to that any complex math or weird data structures, and the whole thing becomes instantly more complex.
Some code, especially driver code, has to be written in assembly. Same for boot loaders or initial starting code, because there are simply some things that are absolutely machine specific, and cannot be represented in a higher level language.
If you write any OS-level or driver-level code or possibly some library code that interfaces with hardware directly, assembly can be inevitable.
Other example is WASM, or LLVM-IR, both are useful to understand the limitations and abilities of the target “machines”.
All true. Similar use case, I’ve worked in a domain that hasn’t required a single line of assembly in 30 years over a broad range of architectures. However, any programmer worth their salt has experimented at least a little with assembly. Perhaps absence of exposure flags a lack of curiosity?
I agree. I remember a lecturer at uni, during our lab exercises ( we were learning how to use assembly language and program with the 68000 Motorola chipset..) joked ‘anybody caught trying to write software in assembly language in 2003 should be shot’
I laughed cos I had remarked how ‘Fun’ it was learning assembly language 😂
Log story short, I did enjoy that module even though I had to repeat it in my second year lol
I absolutely nailed it in my second year and surprised myself at how quickly I could learn it and all self taught ( I’d go in on weekends and use the labs and really understand what was going on…).
It’s compulsory. To learn programming at that level imo. I can’t fathom teaching computer science to newcomers to the subject without making them understand how to manipulate and move data between address and data registers. It’s all a haze now, but I do have fond memories writing assembler code.
“Assembly language isn’t really one language, though. Every chip speaks its own dialec.”
Transparent portable c better way to write machine language in 2023?
For getting things done, absolutely — use C. For expanding your mind, learn a variety of assembly languages. I still claim that reverse engineering, i.e. studying disassembled code for something like a boot rom, is one of the best ways to learn assembly language. We are hackers, right?
+1
Agreed wholeheartedly.
Assembly is kinda like Latin. If you’re learning any Romance languages, or heck any language at all, it’s nice to have for the insight it gives you. But you’re not going to be speaking it often/ever.
Unless you’re doing RE. (Although tools are making assembly less and less necessary here too.)
Last time I looked, the fill bit for shift right in C is undefined. Many processors distinguish arithmetic shift right (sign preserving) from logical shift right (zero fill). Making these portable in C is tedious.
Some processors have separate instructions for rotate and rotate-through-carry. Try doing either in C.
How recently was that? Every C compiler I’ve ever worked with fills the high order bit during right-shift according to the signedness of the expression being shifted.
Unsigned variable contexts zero fill. Signed variable contexts preserve sign by using an arithmetic shift, replicating the high order bit.
This idea of “context” is important, as the standard allows integer promotion: When a subexpression is promoted, its signedness is preserved such that subsequent operations involving natural-size integers have predictable behavior. Subsequent demotion to a smaller precision variable (bit masking to lower-precisiont value) is similarly expected to be predictable.
That said, I would love to hear about a compiler that fails to define arithmetic this way, as it would break a huge amount of code that I’ve seen in the wild.
(I’ve used Whitesmiths C in PDP-11, various compilers at AT&T B.L., SVR3, SVR4 UNIX, GCC (Mac, Linux, bare metal) X86, ARM, PowerPC, MC68HC812, some MSP430)
Right shift in C can give surprising results if the shift count is more than the CPU word size. I have observed this recently using gcc with Debian on x86 for sure, and I think also with gcc on Raspbian.
Good point, that was one of the observations in I read in my quck search.
Any shift equal to or greater than word size is undefined in the spec. (I claim that) When the shift size is a known at compile time, the compiler could produce a competent “zero” or “all ones” depending on signedness.
The trouble arises when the shift size if it’s variable, and I am guessing that compiler developers wish not to have to produce code that checks the shift size on every expression involving a variable sized shift.
That does leave it up to the developer.
I find the AVR platform fun to program with assembly, as it is accessible and have a reduced instruction set. I wrote a blog post showing how to optimize the Arduino “blink” example from 924 to 12 (or 4) bytes using AVR assembly: https://pcfood.net/blog/2023/02/20/ASM-crash-course/
Great post!
Agreed!
Also, love the “slow down the clock to remove the delay” optimization(?). :) You’d never do that in practice, but it’s a nice reminder that clock speed is a choice.
Trying to find that hack where the guy made a program that ran one clock cycle per day by tying the clock pin to a light sensor…
Slowing down the clock to optimize (or fix) your platform is something quite normal. Good example of a function that may only be available through assembly.
Writing in Assembly isn’t portable. But who cares!!!! It is fun. Don’t get me wrong, I write mostly Python, Perl, c/c++, Pascal, and some C# for work and at home. But to sit down and crank out a small ARM assembly program for example, is, well, enjoyable. But then I have a CS degree and have always enjoyed the challenge. Some people only think in terms of productivity…. But as a hobby at home … who cares if it takes a month or two of on/off programming? Time isn’t money there. When I started work in the middle 80s, we had to write serial drivers (68K/x86), boot strap code, cooperative tasking/ real-time tasking modules, and a few graphic subroutines (x86) in assembly too. Also one of the main programs we supported (written before I got there) was a Z80 assembly application for a SCADA RTU. So for work, there were times you had to drop to assembly. Some times turn into a HEX file, burn to an EProm, or later flash. But today, I don’t ever have to do any assembly at work. That era has passed. But at home… assembly programming is still relevant (to me).
Unless you’re counting cycles like in a SIL4 project, you can’t beat C.
Of course, but that’s not the point. Getting down to the actual instructions (without resorting to machine code) and assembling them into a working program is rewarding and fun (speaking for myself). Not so much about writing the most ‘optimal’ code. So why not?
Should add …
Mike said: “you can’t beat C” . Agreed. For modern CPUs it is the way to go for most projects that feel the need for speed and productivity. Believe it or not, Python handles a lot of my projects just fine due to the high speed these ’embedded’ processors run at. Just finished up two little projects with the Pico RP2040 with microPython and they are working great.
If you’re doing scientific work, Fortran is still faster, because it has less concern about aliasing and more support for parallelism. The first can be mitigated by the use of restrict and the second by processor specific vector intrinsics, but then you’re giving up portability.
If you’re not counting cycles, many modern complied languages are as good as C, and even JVM and .NET support many languages less than 10x C. As the Programming Language Shootout showed, while tightly optimized C usually was the fastest, idiomatic C versus idiomatic Ada or Go or Rust often didn’t result in a victory for C.
Thing is though, C is ‘much’ easier/quicker to write, then dealing with Rust’s pickiness, and Ada’s correctness. Never touched Go. C will always be my ‘goto’ compiled language.
I suspect that’s experience with C and lack of experience with the others more than anything else. I’ll take Ada’s pickiness for generics, multiprocessing and a collection library any day. In any case, if you want easier/quicker to write, most languages do that. Perl, Python, Java, Scala, Clojure, Ruby, the choices are endless. I suppose there are certain games that need speed of coding, speed of running the code, but aren’t concerned about bugs so much. Most things, I think, don’t have those particular concerns, and it seems likely that correct and fast Ada or Rust is going to be quicker to write than correct and fast C.
Or if you absolutely need to watch your space like in some retro dev scenarios.
Not just retro. Anything dealing with video will drain battery, kill performance and even exceed space constraints unless it’s well optimized. My android units can’t even handle text input fields anymore thanks to slow languages.
That too. Programs running in a JS VM running in the Dalvik VM. Great idea. Not.
Yeah, they try their best to speed things up with several tricks like just in time compiling and whatnot, but nothing beats stuff running on the bare metal.
If one where restricted with a Harvard architecture that runs its code from some kind of ROM, like building a computer from an AVR, then it makes sense to run an interpreter. One could argue if one should build a computer with an AVR but thats another topic but yeah.
But, staying with the example, ARMs are Von Neumann and even if the OS is burned into a ROM can execute from RAM. So nothing stops one from running machine code, weither compiled or hand crafted, on a device having an ARM CPU. No need for an interpreted language.
And its not even that they run slow, by running trough whatever kind of interpreter they use more energy for the same task.
And if i can save several watts by running my code native on the metal that multiplies with all users of my code.
To say it with a grin: Use C, save the planet!
Last time I needed it was for upholding the timing restrictions for multiple WS2812 chains on an AVR (while doing a lot of other stuff, too). I could interleave the instructions better than the compiler on Os/O3 and also remove a few redundant ones (when you control the stack). But, of course, it was only for a select few functions. Profile first, then optimize accordingly.
I would recommend the MSP430 as an introductory assembly language. Orthogonal registers, just 27 basic instructions; 16-bit; 4 addressing modes: simple to learn, easy to program.
Agreed Julian. MSP430 is a good place to start to learn assembly language programming.
256K byes of FRAM is probably enough for most small IOT embedded apps.
Just want to say that I love the picture accompanying the article!
+1
We’ll take any excuse to re-use good old Joe Kim artwork!
I agree with the “right now”. Also it isn’t so much a different language as it is dealing with the actual processor architecture. There is no point in assembly language for “getting things done’. For that, use C which has been called a “portable assembler” by some. For things where you need to be as close to the hardware as possible.
There are exceptions of course where you really need assembly. Some of the bootstrap code for an operating system really must be done in assembly, and on a rare occasion coding something in assembly provides a performance benefit.
But the real reason to learn assembly is because you will learn how a processor works inside. Of course you can peel back another layer and start designing processors in verilog that will run on an FPGA, but don’t tackle that until you are comfortable with the assembly level.
I just don’t see the “new hotness” in RISC-V. It is just another 32 bit risc cpu. No better and no worse than ARM. Let your pocketbook be your guide. Can you buy a RISC-V on a little board for $2 (I am thinking of the inffamous “blue pill” STM32F103 boards)?
And I’m not all that sure about recommending 8 bit processors. Frankly almost all of those are “weird”. By weird I mean they have a totally non-orthogonal instruction set. Every register is different and there are myriads of special case rules where you can do this with this register but not with this other. I was recently reminded of all this by a reverse engineering project with an 8080. But the 6502 and Z80 are no better.
Something like ARM or even RISC-V gives you lots of registers, all 32 bits, and with maybe one or two registers acting in some special way (like a stack or always zero register) they all act the same.
There are several RISC-V boards around that price point, yes. Pine64 has the 0x64 for $6-$8 (the $8 board can run Linux!) and the Milk-V Duo is $9. I’m not even going to try to explore the veritable glut of boards on AliExpress to find one for actually $2, but those are the ones I’m familiar with.
Beyond that, the “new hotness” of RISC-V is that you don’t need to pay ARM $BIGNUM to get access to the ISA (if they’ll even sell to you as a private individual). Sure, it is just another 32 bit RISC CPU, but it’s a 32 bit RISC CPU without the licensing fees. Of course, you still need to actually design and fab the CPU, which will cost $BIGGERNUM, but it’s still more accessible. Something we are seeing, though, is the popularity of implementing RISC-V in FPGA and emulators. QEMU’s RISC-V support is quite solid. This is part of why so much open source software already Just Works for the architecture, and part of why Debian is considering promoting RISC-V to an official architecture for a future release (trixie, I think, the next one, but maybe the one after that).
None of that licensing fee stuff matters to me. I know about all of that. Could care less.
Unless you intend to start fabbing chips, it doesn’t matter. It is just “feel good” hype.
The ARM licensing model generally charges (a usually small price) per CPU core.
There are plenty of research projects and plenty of production systems that contain way more CPU cores than are visible to the average device developer, and the fees begin to add up. Separately, just tracking the license counts is starting to add up too. It’s probably not a large fraction of project cap-ex or op-ex, but it does create friction.
So for you and me today, ARM is just fine, as are X86_64, etc. sometime in the future, people at our level are likely to see these multi-core systems even at the “$2” price point.
I expect that the open licensing model will offer additional freedom to innovate, with less friction, in places where that freedom is currently limited.
Oh, and I should add that RISC-V isn’t just 32 bit, it goes all the way up to a theoretical 128 bits. 32 bit RISC-V tends to be more common in microcontrollers, whereas the 64 bit variant is of course more common in devices designed to run consumer OSs like Linux.
When confronted with a “weird” ISA, the general approach is to create a VM, that is “less weird”.
This can be achieved in about 512 to 2048 bytes of assembly language – on almost any 8, 16 or 32 bit processor.
I helped create a minimal interpreted language called MINT (Minimal INTerpreter) that fits into 1700 bytes on a Z80 including serial I/O communications. There are plans to port it to AVR, MSP430, 6502 and RCA 1802.
MINT creates a 16-bit VM with 26 registers. MINT uses printable ascii characters as a bytecode language – so it retains a high degree of human readability. You can get a lot of MINT program functionality into a 160 character SMS message.
Although every instruction set architecture (ISA) has its own personality, most modern assemblers follow the classic “two pass” assembler pattern set down in the early 1950’s. If you know how to write code for Roy Nutt’s “Symbolic Assembly Program” for the IBM 704 and you add to that knowledge of your modern processor’s ISA, you’ll be able to target it with assembler.
I have always thought that there is really nothing to be scared of when it comes to assembly language. Computers are actually pretty dumb: they can do addition, subtraction, [multiplication, division,] bit twiddling, [conditional] branching, reading/writing memory, and not much else :)
I am not a great assembly language programmer but I do know ARM/Thumb/Thumb-2 assembly language a bit. In the last decade I have used that limited knowledge to:
* Debug optimized code where source level debugging left me more confused that just looking at the disassembly and registers directly. This is probably the main thing for which I have used this knowledge and I have done it on several platforms in the past (ARM, x86, PowerPC, etc).
* Write Hard Fault handlers for Cortex-M microcontrollers to dump the CPU state (registers, RAM content, etc) to enable post mortem debugging.
* Write a debug monitor for Cortex-M microcontrollers.
* Make changes to the context switching code in an open source RTOS.
* Reverse engineer a closed source iOS library.
I would say that I read assembly language code a lot more than I write it.
“I would say that I read assembly language code a lot more than I write it.”
A man after my own heart …
As it is with most second languages :-)
Let say I want to learn assembler for Arduino Uno, do you guys know some good books or links to start with?
Search on “AVR assembly” and you will find many links and tutorials. I can’t speak for how good they are or which are best. And I would look for some AVR assembly projects on Github and look at what other people have done. A search like “AVR assembly github” took me quickly to a bunch of interesting projects. One fellow did the usual blink the LED in assembly.
I will. However, ,sometimes I get the filling of missing the key part of the whole thing I am learning when I follow tutorials , maybe I got so used to learn from books, right now I’m trying to get some good use of Freecad but it seems like I’m going down the learning curve :) So the blinkling led sounds like a good start tho. Thank you!
This is a great collection of posts. I had to learn assembler long ago for OS 360 and Univac 1108 and even an Olivetti Programma 101. Everything else was easy after that. Cheers to all.
Bless you, IEFBR14, and thank you for reminding me that the original IEFBR14 had a bug. The original code was simply a BR 15,14, but that failed to set the return code. The revised code added an SR 15,15 to set a return code of 0.
Since I work with industrial applications, timing oblige me to go bare metal, so assembly is a must.
I’m 32 year old, so I’m from the high level generation, but I can’t get rid of assembly as I can’t get rid of analog electronics in my projects, due to simplicity of analog circuits to many stuff.
But most engineers in my age says low level and analog are too hard to work… The point is: we are kinda “de-evoluting”, soon we’ll have nobody to deal with this more detailed stuff, and that should scare mankind.
As an EE approaching 60, I respect your tenacity for sticking with assembly language coding and analogue electronics.
We live in a very complex world, but somebody has to understand how stuff works and is prepared to do the “hard work”.
We are all standing on the shoulders of giants.
First assembly I learnt was from the C64 Reference guide in the 80’s. Then Microchip pic in the noughties.
Very small instruction sets mind you. I tried over the years to learn X86 but found the size of the task and oddness of instruction set and segment addressing tiresome.
So learnt C/C++ and Python and love the productivity and easy code reuse but am at present finally deeper into X86 assembly learning. I saw Dave at Dave’s Garage do a speed test with a prime number sieve on several languages recently. It was open for submissions and I was disappointed no one from the assembly side could come up with an example in assembly that could beat the other languages. It’s crazy considering that underneath those other languages it’s all just assembly. Either the right person hasn’t seen this competition or the task is really just that hard to beat compilers of today. Anyone keen to take up the challenge?
I saw this too, and agree. Especially with something “easy” llike a prime sieve, it’s a strange result.
I mean, you could take the winning entry (Rust?) machine language and just disassemble it for the no-effort tie.
If you could find a single tweak that’ll beat it, you’d win. Not saying that’s easy.
I guess that the people who are able to program in assembly are just too pragmatic to try to prove a thing that is obvious.
I also agree with you that x86 assembly is tiresome. The segmentation stuff is from hell (yes it was probably necessary in 1976). I much prefer 68k. And for fun any MCU – PIC, STM8, AVR…
IMO assembly is fun to learn and play with–though do yourself a favor and stay far, far away from x86 assembly. I tried learning it and found it absolutely inscrutable. As soon as I started looking at ARM (and later RISC-V) assembly, though, everything made sense. x86 has five decades of legacy, but ARM and of course RISC-V don’t have that. That, and they’re just designed better. Oh, and of course, any mention of ARM assembly should recognize Sophie Wilson, a true pioneer in computing.
And assembly is practical, too, for all the reasons people here have mentioned. Maybe don’t implement string handling or networking on your own–that’s why you have a C library–but doing arithmetic and logic in assembly is just fun. It really illustrates how programming is all about breaking down a problem into parts that are simple enough that even a computer can do it.
Yes, the world runs on ARM chips.
We should acknowledge the outstanding work of the original ARM team of Sophie Wilson, Steve Furber and Herman Hauser et al, that has made the modern world possible.
+1
x86 assembly is an unhappy place
choose another platform and you can have a lot of fun and satisfaction
I really enjoyed learning assembly for the Nintendo Gameboy cpu :D
There you go! That’s a good target b/c it’s fun along the way.
How are the GB emulators / VMs coming along these days? That would be an awesome learn-assembly playground.
The first assembly language that I learned was the DEC PDP-8 in the second freshman engineering class at the University of South Carolina.
Self taught: DEC PDP-11, Intel 8080/8085 (Zilog Z80), IBM 360/370, and Motorola 680×0.
I’ve done assembly on the R6502, and the X86 platform. And certainly the Z80. Also made use of MSP430 languages, (both C and assembler) via their tools. I’ve looked at what the PDP-11 uses behind the scenes. On the whole I found R6502 easy to use, the X86, and the Z80 and looked at the PDP-11. But I found I370 ones easier to grok than all of those.
4 decades ago, 1987 to be exact, I became fascinated with an Apple //e program called Diversi-Dial.
It was slow, 300 baud, that allowed 7 modems (8 total including the console) to chat. I bought and ran
my own station, and still to this day I like looking at the 6502 code. There is a more modern recreation
at https://www.magviz.ca written in a more modern language. While 300 baud is slow, 6502 can be tedious, and the old software has been recreated in a sense with modern technology, I still have a fondness in my heart for that old 6502 program.
Cool! 🙂👍 My father had an acoustic coupler in those days (predecessor to the telephone modem).
Btw, there was Packet-Radio on the ham bands back in the 80s.
300 Baud (AFSK) on shortwave, 1200 (AFSK) to 9600 Baud (FSK) on VHF.
That would have been fun to tinker with. Upto 8 simultaneous connections were supported by PR programs, I vaguely remember.
After attending hardware training on 2nd generation computers, where we had to program in machine language assembly seemed like cheating
Comments about assembly language make me think of the ARM Cortex-M series, and the early NASA rocket failures.
The ARM has some fused Multiply Add instructions, that let you consider a single 32-bit register as holding 2x 16-bit values, or even 4x 8-bit values.
One of the early NASA rocket launches had mismatch along the sensor / ADC / controller path for altitude readings. At a certain height, the rocket “thought” it was actually underground, and attempts to correct this destroyed the rocket.
Conclusion? The format of the data you work with comes first. If a High Level Abstraction language causes you to lose sight of that, that’s on you.
And if you switch to assembly but fail to test for overflow etc. errors, that’s on you too.
Let’s please also note that there are Assemblers and Macro Assemblers.
The latter is what we take for granted nowadays, but back then it wasn’t being taken for granted.
I typically want to get further away from the hardware to make my life easier. But I appreciate the use case for understanding what high-level language turn your code into. That said that’s about all I can appreciate.
Whilst we generally think of C as the universal language these days, I am of an age (approaching 60) where that was not always the case.
C took off, in the mid-1980s, when the IBM PC brought affordable 16-bit computing to business, industry and academia. These machines had sufficient resources to host a C compiler, one popular package was Borland C, from the early 1990s.
Learning assembly language should be seen as part of the process of familiarising yourself with the instruction set and architecture, ISA of a particular CPU.
Twenty years ago, I wrote a set of programs in PIC assembly, to generate and receive and decode DTMF and V23 modem tones for a telecom product that had to respond to a Caller ID signal. These tones had to meet FCC and EU telecom frequency, timing and amplitude standards, and the only way to achieve this, was hand assembled PIC code.
For the hackers amongst us, the PIC tone generation could also generate the “Nickel Tones” as used in US payphones! Captain Crunch rules again!
Learning assembly language should be seen as part of the process of intimately acquainting yourself with the instruction set and architecture, ISA, of a particular CPU.
Charles H Moore, the creator of Forth, did this numerous times, when he ported Forth to many mini and mainframe computers.
He would study the instruction set, write a set of primitive routines, and build a 16-bit virtual machine, that would interpret and execute the Forth language. Generally this process would take him about a week, to port Forth to a completely unknown machine architecture.
Writing a tiny interpreter, for a new machine, is a good way of learning its assembly language and ISA. A tiny interpreter can be written in as few as 1000 lines of assembly language, which is between 1000 and 2000 instructions approximately 1K to 2K bytes.
Steve Wozniak, famously wrote SWEET-16 for the Apple II. A 16-bit VM that fitted into about 400 bytes on the 6502.
There is a tiny Forth for the AVR which fits into 2K ROM space, and if you are oldskool there is VTL (very tiny language) for 6800, 6502 and Z80/8080/8085. For the RCA 1802 there is CHIP8, which codes into only 512 bytes.
There are also a few tiny interpreters that fit into the 512byte bootsector of an IBM PC disk.
Some years ago, Hackaday hosted the “1K Challenge” – write the most interesting app in 1024 or fewer bytes of code. A tiny interpreted language can be done in fewer than 1024 bytes.
Try doing this in C, where blinking a LED on an STM32F4xx takes about 22 Kbytes of codespace! (YMMV)
IMHO, every coder should have a knowledge of assembly language – in order to earn their “stripes”.
Assuming that a machine is a blackbox, with infinite resources, and having access to numerous libraries, does not make you a good code writer. Cut and paste coding just leads to bloatware.
We are only here, because we are standing on the shoulders of giants!
I used to program PIC controllers in Assembly for a few years for hobby projects. They were cheap, widely available, had a well developed IDE, assembler, simulator, and a library of application notes that showed how to do almost anything you could imagine. Then Arduino boards came out, and for reasons I still don’t understand, became extremely popular with hobbyists in spite of having a crap IDE (every time it updated, your old code would no longer work).
I’m going to have more time for hobby projects soon, and will reevaluate Arduino and PIC stuff to see if there’s any reason to choose one over the other these days.
As a hardware engineer, approaching my 60th revolution around the sun, I can say that assembly language was essential to my early career in understanding how computers work.
I am reasonably fluent with Z80 and MSP430, but there are only so many mnemonics that you can hold in your head.
I know enough C code to be dangerous, but I struggle with all the modern C++ derived languages.
Forget fear of programming in assembly, FOPA.
My biggest problem these days is FOLP. Fear of learning Python!
Don’t be afraid! There are like 5 new “C replacements” every year. I just ignore them. Nearly nobody uses them, just the evangelists are very loud. Forget Rust – Zig is the new cool kid on the block. Javascript? No, Typescript! Java? Meh, Kotlin! Where is Ruby? It was the hottest thing a few years ago…
I’ll probably skip Python and learn directly it’s 5th replacement. In the meantime I’ll stick with C, Bash and PERL, they are old and boring, but I already know them and they perfectly do whatever I need.
And of course assembly for some fun ;)
What assembly language are you using? Java released 28 years ago, and every 64-bit assembly language in use is newer than that. Heck, why do you use C, a language that’s barely 50 years old, and is a BLISS or ALGOL replacement? Java, JavaScript and Python are all more than 25 years old, and Python in particular is only four years younger than Perl. Unlike C, Java, JavaScript and Python, which have been on the top of lists of languages used for a long time, Perl dropped off the top of those lists some time ago.
As much as i love me some Perl, it is the worst example here. In several benchmarks it showed up as one of the slowest and hence the most energy inefficient of the lot.
Even my most favorite “interpreted” web server side language is in the middle field, Dotnet.
If i where to take Green IT to the point and not only as a guideline i would rewrite my little blog in plain old C.
Check out the paper over here:
https://haslab.github.io/SAFER/scp21.pdf
I recently built a Ben Eater 6502 breadboard computer and have been learning to use assembly.
Aside from being a fun project, learning assembly has given me a much better understanding of programming in general. I now believe all programmers should spend some time on it. It doesn’t mean everyone should spend hundreds of hours programming complex programs, but some time spent fiddling with cpu registers and directly manipulating memory goes a very long way in understanding the underlying architecture of every computer.
Also, the restricted performance envelope and limitations of these old processors like the 6502 allows you to easily experience things like the tradeoffs you can make between code size and performance.
Think of it like taking a semester of Latin or Spanish or whatever.. Sure, your not going to the Vatican or Spain and talking like a local…. But you would be able to find a bathroom and maybe get a place to stay… Assembly programming is about life skills, not lifestyle.
Well said
The assembly language is the key for finding bugs and the closest way to see what the hardware do.
I saw many firmware written for embedded devices in C or C++, where was necessary to use a powerful 32 bit microcontroller, for simple tasks, because the extra layer of HAL (hardware abstraction layer), when a little micro of 8 bits do the same and more efficient.
Its a nice learning experience if you want to take the time.
Here is something for those who want to learn machine, take your favorite 8 bit CPU, make a nice little SBC out of it with serial and an SD card and write a simple OS for it.
For the board there is enough inspiration on the net if that isn’t something you whip up from scratch in an hour, but then the fun part begins.
In lieu of such a board you could also use a simple emulator and start with that, i could whip up a simple Z80 emu from parts in an hour. Just the CPU and a simple ASCII in and out that you can reach from IO space for a start, can add some form of mass storage later.
1967 PDP7 LASA project, 1000s of lines. I think programmers should know / understand Assy. language to appreciate higher coding.
#hottake potentially #unpopularopinion If you fancy yourself a true hacker, and not just another hipster maker that only knows what the instructables tells you, misappropriating the word because you think plugging premade breakout boards into a solderless bread board and running some copy pasta code is a ‘hack’ since you can see the PCBs, then OF COURSE ITS WORTH UNDERSTANDING ASSEMBLY CODE!!!*
*”Understand” is being used in a very loose, broad context. You dont have to commit all the architectures to memory, and all the various instructions, and all the caveats to those instructions. Hell, you dont even need to be able to read/translate assembly. But one who appreciates technology on the level a hacker does should be aware of the workings of digital electronics enough to have an understanding of what assembly is doing. It is literally the last step before bare metal shuttling charges around. And the first step above the ‘state machine’ like ‘dumb’ logic of basic circuits using discreet components (or discreet circuits condensed into an IC like a 555 for instance) where we have put lighting in the rock, and begin teaching that rock to think.
Yes, its complex and messy. But so is everything else in electronics and computing. Assembly is most certainly worth understanding. Anyone who thinks otherwise is spending too much time sniffing their own farts, riding the high of assuming their opinion of something they dont respect is in any way indicative of what the community as a whole believes (looks sternly at HoD writers and editors….).
Learning assembly language might seem daunting, but it’s like learning the secret language of machines. It opens up a world of understanding in computer architecture. Embrace the challenge, and it’ll make you a better programmer. Great article!