In my day-to-day work, I mostly communicate with computers through high-level languages. High level languages are designed such that instructions are written with a small subset of vocabulary we use in day-to-day life. For instance, we use the keyword for / while / map etc. to repeat certain operations, defining function or subroutine for repeatable tasks.
However, this is not how computer works. Few years ago, I signed up to a course explaining how computers are built. We eventually built a imaginary 8-bit CPU (but some people programmed an FPGA according to the spec) through carefully assembling logic gates (which are a series of electronic components). The CPU recognizes only 2 states, either yes or no, either through presence of electric current, or through the amount/level. Aligning them in proper order, through the input interface, will yield results in a set of yes and no on the output end.
Therefore the input is a set of yes and no, on and off arranged in very specific order, and we can eventually represent them as a series of 1 s and 0 s, which is also known as the machine code. However, while it is doable, it is not very convenient. Hence, from here we build layers of abstractions / tools to make it easier to instruct the CPU to do computations.
The first thing we built was an assembler, which is pretty much a translator to turn assembly code into machine code. The assembly code is pretty much a direct translation of machine code in a slightly more readable way. So when we say low-level programming, we usually mean either writing raw machine code, or just assembly.
Learning to code in assembly does makes me appreciate a lot of things when I code. Arguments about whether we should follow this paradigm, or that design pattern is meaningless because nothing is pure in the low level programming space.
With the new found knowledge, I eventually discovered some games that introduces low-level programming to the general public. First was Human Resource Machine, which simplifies the language into a list of commands you can drag-and-drop to the editor.
Image credit Tomorrow Corporation
Going through the game was a fun experience, it reminded me a lot of the times I struggle with the nand2tetris course I mentioned previously. Then a few years later, I saw another game, and got into it out of curiosity.
Exapunks sets the game in a cyberpunk world where a disease slowly turns human beings into computers. In some of the puzzles, one can even fix oneself through the computing interface exposed and link it to a computer.
One proceeds the game through solving the programming puzzles presented. Sometimes the game requires to read a file, and write it to a register elsewhere, scan for some information scattered across the network, send it back and write to some register etc. The challenge is that the number of available commands and even the number of registers are limited. No more spamming endless list of variables to hold intermediate computed values.
How does that benefit one’s programming skills? It probably doesn’t, in the end, like other programming challenges, the thought process matters. The written code, is just a mean to represent our thought process. On the other hand, knowing how values are being computed in the hardware, helps when you learn some languages, for instance Rust and the ways they ensure memory safety.
Meanwhile I do have a life-streaming archive of solving the puzzles. While I do not talk much, it may serve as a reference to the future posts.