Over the last three months, I’ve developed an unusual little nighttime routine: after reading bedtime stories to the kids, I hop into bed and instead of reaching for a fiction book, I’m turning on my 14-year old Asus eeePC 701 miniature laptop and writing x86 assembly language. This is turning out to be an insanely good use of my time…
My interest in the Zig programming language has driven me into a deeper interest in "systems-level" programming and that, ultimately, seems to lead to computer architecture, instruction sets, and assembly languages.
For reasons I can’t explain, it just feels right to use my tiny computer with its tiny keyboard to type tiny instructions late at night. I suppose it stands to reason that being in a completely different environment from my normal desktop development machines put me in a receptive learning mood.
Also, though I did install Lynx, this is not a great machine for modern Web surfing (except for ratfactor.com, of course). So it’s a pretty distraction-free device.
Since this little computer is used for nothing else, I’ve been slowly
customizing it for this one task: when I log in, my
immediately does a
cd to my assembly project directory.
I have a bunch of little cryptic one and two-letter scripts and aliases that open my project and related documentation in Vim, or start up the GDB debugger and run a GDB script with a breakpoint at the last thing I was working on.
These little things make it so much easier to get going when I’m tired and not feeling like expending much effort. Now I’m at the point where grabbing the eeePC, logging in, and opening the assembly project is automatic, like brushing my teeth.
So, what’s this project I’m working on?
Immediately after completing the lessons on asmtutor.com (see NASM under Resources below), I immediately began on one of the "bucket list" projects I’d shelved many moons ago: reading and understanding Richard WM Jones’s JONESFORTH.
There are a couple mirrors of the JonesForth source on Github. Here’s the copy I grabbed: https://github.com/nornagon/jonesforth
But not content to merely read it, I’ve decided to port it to NASM.
The progress has been incredibly slow because I’m new to assembly in general and NASM specifically. JonesForth is written in GAS (the GNU Assembler) with a completely different syntax. Even the machine instruction mnemonics are different because GAS often includes the size of the data being manipulated as part of the mnemonic.
(Check out Why no one should use the AT&T syntax ever, for any reason, under any circumstances for a more thorough rant than I could hope to write about the syntax.)
The biggest initial challenge was porting some vital macros from GAS to NASM. Each one took weeks to understand and convert.
But it’s pretty awesome and satisfying to be working on something non-trivial, even if it started way over my head. I’ve wanted to give up on this so many times, but I just keep grinding away at it and the barriers are slowly giving way. Every time I figure out what a new instruction does or fix a bug, it’s a little triumph.
Okay, here’s the repo with my progress:
In particular, check out the log files (
log02.txt, etc.). They
document my trials and tribulations. I wish I’d been doing them from the start
but I’m glad I’m doing them now.
Slow down, Enjoy the process, Learn the tools
This section is an update:
I "accidentally" put a deadline on the JonesForth port recently. I want to finish off a bunch of the little projects I’ve accrued for myself this year so I can more single-mindedly focus on a few bigger ones going into the next year. Well, as soon as I had that deadline, you know what happened? I started trying to hurry through blocks of JonesForth assembly, converting them to NASM as fast as I could. Stupid, right?
I knew I had to force myself slow down. But that’s easier said than done. A sense of urgency is easily gained, but not so easily lost.
My "shower epiphany" last night (this is days after writing this original article), was how to go about slowing down. And here it is: I’m going to stop where I’m at in the translation, fix any syntax errors so the thing assembles, and then walk through every single instruction I’ve got so far and make sure it all works as expected. And to make that more enjoyable and actually have something to show for my efforts when I’m done, I’m going to log the whole session. I don’t even have X on this computer, so the obvious choice for logging is GNU Screen, the terminal multiplexer.
My first thought was, oh great, now I have to learn another tool and get completely sidetracked!
Then my second thought was, hold on, that is great. Now I can finally learn
another big ubiquitous GNU application properly and have another tool I’m
confident using. Plus, I think Screen will be useful for all kinds of
convenience improvements: I’m currently using Linux’s virtual terminals
Alt-F2, etc.) to have a Vim session and GDB session both open for
easy reference. I have to manage those manually. With Screen, I’m pretty sure I
can automate opening both things with a single command and then be able to
toggle between them with Screen’s
So I’m currently reading the entire GNU Screen User’s Manual and trying it all out so I know what my options are. Then I’m going to log a big old GDB session and turn it into a transcript which can go into the repo as another artifact on the journey. Now I’m imagining a series of brief session transcripts in which I explore each new bit of functionality. I think this will be really neat.
(Update: I’m doing this! Check out the log files in the repo as mentioned above.)
The point is, I sort of lost my way for a moment, thinking of the finished Forth port as the "point" of this exercise. It’s not. The learning process and the skills I acquire are the point. Plus, after the port is done, Assembly Nights are going to turn into Forth Nights. I’ve got Leo Brodie’s Starting Forth and Thinking Forth on my nightstand. So this journey will take however long it takes and it will be glorious.
The NASM assembler comes with documentation in the form of a single text file. I have this file open with my assembly source file so it’s always just a Vim buffer away. It’s the same content as this HTML version.
But where to start?
My path (which I highly recommend, by the way) has been to first learn the basics of assembly with the clean "Intel-style" syntax of NASM. For that, I very highly recommend:
https://asmtutor.com/ - NASM Assembly Language Tutorial by Daniel Givney
You’ll need a Linux computer (or I suppose something virtual like WSL on Windows) since the tutorials rely on Linux system calls.
I did all of the exercises on the eeePC, under the covers, with asmtutor.com open on my phone! It’s just as naughty as it sounds.
What I love about asmtutor.com (besides being exactly what I was looking for and completely free) is that you end up covering a ton of functionality (from Hello World to a multi-threaded Web server) in bite-sized lessons. You also end up building up a utility library of little functions which you can use as a jump-start on other projects.
I gained a lot of understanding from these lessons in a very natural way - through use. Each lesson is a handful of explanatory paragraphs and then an assembly program. I always tweak and modify examples to make sure I understand them, so my utility functions started to diverge from the originals pretty early on.
ret over and over in these exercises
did more for concretizing these fundamental concepts than years of theory and
programming in high-level languages. Wow, I kept thinking, I should have taken
the assembly dive years ago!
(Actually, I read a DOS-based x86 assembly book and enjoyed writing DOS assembly
programs using nothing but
debug waaaay back in my youth, but that was before
I’d really done a whole lot of programming. So the experience hit and stuck
differently this time around.)
GDB is a huge and intimidating piece of software (practically an operating system), so I went ahead and purchased the physical paper book Debugging with GDB: The GNU Source-Level Debugger direct from GNU Press/FSF. I read the book on weekends from the comfort of a couch and bookmarked things to try when I got on the eeePC that night.
Of course, the GDB manual is also free to read online here: https://sourceware.org/gdb/current/onlinedocs/gdb/
x86 Assembly Language
There is a really nice overview with a list of common instructions written by Adam Ferrari. I recently discovered that it exists in "Intel-style" at the University of Virginia:
and in GAS/AT&T syntax at Yale:
I also have a physical copy of the highly recommended Programming From the Ground Up: An Introduction to Programming Using Linux Assembly Language by Jonathan Bartlett. It uses GAS. Logically, I should obviously have started with that, then done the asmtutor.com tutorials for NASM, and then started my JonesForth port…
…But I actually believe the book is going to stick so much better now that I’ve already learned some of this the hard way. Which perfectly fits my pet theory about learning: The Phenomena of the Game Manual.