Learn Assembly Programming the Fun Way
A discussion of games to learn assembly programming and why learning assembly is still relevant today.
Have you always wanted to learn assembly programming but think it looks too complicated? Here I will go through some ways to learn assembly programming the easy way while having fun.
Just to clarify, this is not intended as a way of learning real world assembly programming such as how to program an Intel x86 or ARM microprocessor.
No, this is about the basic concepts that are universal for the assembly code of all different microprocessors. The principles that help you understand how a microprocessor works.
Isn’t Assembly Coding Outdated?
Yes. So why learn it?
Even if you programming a high-level programming language, the code you write has to be translated into assembly code (or more specifically machine code). If you have absolutely no idea of how the computer runs code, then you have no good conceptual idea of how your high level code will perform either.
It is also a question of getting the satisfaction of understanding how computers work from a high level language all the way down to assembly code and down to the microprocessor executing machine code.
My interest in this began with teaching my kids programming. I am teaching them some Julia programming, Scratch and MakeCode. However this is very math like and abstract. It doesn’t give my kids a good sense of how computers actually work.
Specifically I wanted to teach them what a microprocessor does. How it works with memory etc. Assembly code is really good for that. You don’t learn Assembly without learning about microprocessors.
So here is a list of ways to learn assembly programming and my experience with the different options.
The Little Man Computer
Turns out there is this strong simplification of a microprocessor that has been around for a long time called “The Little Man Computer”.
The reason for the funny name is that, instead of explaining a microprocessor in terms of electronic components connected with electric wires, it instead imagines the microprocessor as a sort of box with a little guy inside.
This guy runs around and carries out instructions, the way a CPU would carry out the instructions of a program in memory.
It is a simple enough concept that a whole bunch of simulators for this CPU has been made and people have made games around this idea. Let us look at the games first.
Human Resource Machine
This game is suitable for kids as they find it quite funny with the characters. However it is not an easy game. I have to sit with the kids and help them out.
Basically the idea is that your main character is a hapless office drone which has to carry out menial office work to the letter.
You provide a set of instructions, which look like assembly code, for your office drone to carry out. You need to give the instructions which completes a task given by your boss.
As you succeed, you move up to higher floors in this giant office building and get more advance tasks, which just means you get more trick problems to solve, which require more sophisticated programs.
It is quite similar to the Little Man Computer concept with some twists:
- You don’t have a single CPU to program but multiple communicating with each other.
- You don’t have storage memory. You have a few CPU registers you have to use in a smart fashion.
The whole TIS-100 concept is that you are in the future and TIS-100 is this massively parallel supercomputer thing. You got it from your uncle which passed away. Your job is to fix it to figure out what it was meant to do.
Fixing in this case means programming various CPUs inside the TIS-100 so they run their test program or diagnostic programs correctly. Basically you get a bunch of inputs and have to make sure your program produce a particular kind of output from this input.
From the screenshot you can see that each CPU (or node) is connected to a bunch of other nodes. You can write small assembly code programs inside them. The assembly code is very similar to Little Man Computer (LMC).
Here is an example:
MOV 8, ACC # Put number 8 in accumulator
MOV LEFT # Add number from left node to ACC
MOV ACC, RIGHT # Move number in ACC to right node
It is pretty quick to learn the assembly code. There are not a lot of instructions. You only have two registers
You only got these categories of instructions:
- Simple add and subtract. There is no multiple, divide or logical operations.
- A bunch of different jump instructions. They don’t depend on any flags, only the value of the accumulator (ACC)
- Move instruction moving data between nodes, from input, or to output.
Shenzhen I/O expands on the ideas developed in TIS-100 but makes it into something that looks a bit more like a game.
The back story here is that you are hired to make crappy little electronic devices in Shenzhen, China.
It cold be stuff like a fake surveillance camera, or a billboard which has to animate some lights.
Unlike TIS-100 where you just take in numbers and produce some other numbers as output, here it feels like you are actually controlling some hardware in a useful manner.
Programming in this case is quite similar but with a few twists separating it from TIS-100:
- Instead of uniform nodes with the same abilities. You have little micro controllers with different output sizes and memory for assembly code.
- Unlike TIS-100 you explicitly connect these microcontrollers to each other.
- Because we deal with real time systems, timing is now an issue. Hence there is a sleep instruction
Some of the behavior of the assembly code in Shenzhen I/O reminds of what is called conditional execution on the ARM microprocessors. Here is an example from the ARM manual:
ADD r0, r1, r2 ; r0 = r1 + r2, don't update flags
ADDS r0, r1, r2 ; r0 = r1 + r2, and update flags
ADDSCS r0, r1, r2 ; If C flag set then r0 = r1 + r2,
; and update flags
CMP r0, r1 ; update flags based on r0-r1.
Basically you can a suffix to ARM instructions to make them conditionally execute. E.g. an
ADD instruction will add two number every time. While an
ADDEQ will only add if the previous two numbers tested where equal (EQ). If you want it executed if they are not equal (NE) you would write
Shenzhen I/O assembly code has a twist to this.
teq acc 0
+ teq p0 100
+ mov 1 x1
- mov 0 x1
mov p0 acc
In the small program above you can notice there are (+) and (-) labels in front of some instructions. These are for conditional execution.
teq acc 0 tests if the
acc register is equal to 0.
+ teq p0 100 instruction is only carried out if the previous test was true.
- mov 0 x1 on the other hand, is only carried out if the previous test was false.
If you are an old school assembly programmer like me who learned Motorola 68000 and Intel x86, this looks odd. But modern RISC processors which try to avoid stalling their pipelines can benefit from this.
Although even more modern RISC processor such as RISC-V does not have such instructions.
I just mention this because while interesting that it is included, I am not sure I want to teach my kids these kinds of concepts. I like to keep stuff simple with Jump instructions. Although the conditional execution does make a lot of code easier to write. But it forces you to learn yet another concept. Hence not a big fan.
Little Man Simulators
Because LMC is so simple, I implemented my own LMC simulator, assembler and disassembler in Julia. You can check it out here. It is just 200 lines of code.
To quickly try it out you can also you one of these two web based simulators:
- 101 Computing Simulator. Looks fancy in terms of appearance.
- Robo Writer Simulator. More plain and primitive looking, but I actually prefer this version.
This was a simple CPU made to work using cardboard back in the 1960s. Since then they have made a simulator for it.
This also as a very simply design as you can see, with very few registers. It only has 9 different instructions.
Read more about Cardiac here.
Notch’s DCPU-16 CPU
The creator of MineCraft came up with a simple CPU for a future game, that later got abandoned. It was a CPU designed for being able to easily play with. So it has a fairly small instruction set.
One of the interesting things is that his specification contained the idea of a memory area for graphics, which meant that using DCPU-16 emulators or simulators you could make simple bitmap graphics. The internet is full of various DCPU-16 emulators that different people have made. Here is one example of a DCPU-16 emulator on a webpage.
SET b, asets
ADD b, asets
a + b
IFE b, aperform next instruction if
b == a
IFG b, aperform next instruction if
b > a
This is an interesting topic and the next article I want to write about this will be about how a simple computer/CPU is implemented in electronics.
I will try to base this article off simplecpudesign.com which implements a really minimum complexity CPU.
But I will use the opportunity to talk a bit more about how you actually put together the AND, OR and NAND gates to make stuff like adders, decoders etc that a CPU is made up of.
I want to get into more details on e.g. stuff like how does a CPU decode an instruction and toggle on different functions in the ALU (Arithmetic Logic Unit). Don’t worry if none of this jargon makes any sense to you now. That will be the point of the next article. To explain a lot of the CPU internals.