A selection of games with low-level programming

TL;DR

Games from Zachtronics:

About creating a processor from logical elements to writing code in assembler:

If you like any of the games listed, you'll probably like the others too. I've played 30+ hours of each, had a ton of fun, and learned something new.

The first three games are from the same person and steamship publisher Zachtronics(Zach Barth), and in some ways are similar to each other.

Each game comes with pdf magazines in addition, and you should read them – there will be a description of assembler commands, features of the processor, etc. I will also say that they add atmosphere to the game. This was especially felt in SHENZHEN I/O: I opened the game on one monitor and on another pdf with description of components operation. It felt like I was designing a real microcircuit and, in general, like a real developer, I was designing something (ahem, ahem, as if I don't have that IRL). On the seventh page of the manual there is a visa application form, just the thing for immersion 😛

I'll also say that all the games have humor and references, for example about surveillance in China and the constant smog.

At the end of the level, histograms of the results of all players are shown for three parameters – for example, for Shenzhen i/o, this is the cost of the components of the microcircuit, its operating time, and the number of lines of code. In addition to the formal solution of the problem, you can also try to find the best solution for some parameter or at least beat half of the players.

All three games are a clear demonstration of the fact that it is possible to make a good game alone. Even if it is small, even if it does not have 3D graphics and raytracing, but it is interesting.

TIS-100

The peculiarity of the game is that the calculators in it are unusual.

They are arranged in a 3 by 4 grid and can communicate with neighboring computers.
The cores themselves are extremely primitive, they can't even multiply or perform bitwise operations. They literally have two registers, and the second can only be swapped with the first, they can't even be added together without help from neighboring cores.
The kernel only holds 15 commands.

Everything is well visualized, the context is placed on one screen (states of all 12 cores). This helps a lot in debugging.

It's funny that data transformations can be decomposed into a pipeline by kernels and get decent speed when each calculator does a couple of operations and passes the result on.

The later levels contain some pretty tough problems like sorting an array of numbers, which, combined with very primitive calculators and program size limitations, becomes a serious puzzle.

An example is dividing two numbers.

Exapunks

The game has a cyberpunk future atmosphere. You can read the hacker chat and choose levels to complete.

The role of calculators is performed by nanobots. I would say that they are analogs of processes – nanobots can fork, die, walk back and forth, carry files with them and exchange signals.

There are no program size limits in this game. I played for 67 hours, but I still haven't finished it – there are too many levels.

Shenzhen I/O

It is necessary not only to program, but also to place elements on the microcircuit and distribute tasks between them. Just like in tis-100, but we choose the number of calculators and the configuration of connections ourselves.
In my opinion, a very successful combination – a mix of programming and spatial puzzle. Towards the end of the game, there is sometimes not enough space and it becomes difficult.

There are two types of tracks – for analog and for discrete signals. And if the microcontroller suddenly lacks the necessary pins, you will have to install another one and somehow communicate with it.

I liked the assembly language – it's more convenient, you can even multiply.

It has conditional execution like arm!

Comparison commands set a conditional flag. Commands prefixed with + or – are executed only if there is a suitable flag condition.

For example, if the number is less than 0, it will be multiplied by -1

  tlt acc 0
+ mul -1

And here is a check to see if acc is equal to zero or one:

  teq acc 0
- teq acc 1
+ mov acc dat

This is a bit of a brain-breaker, but in the end it turns out much more compact than with code jumps. In the code after the comparison, you can skip or optionally make commands mixed with the usual ones, and so on until the next comparison.

Of the commands, I'll note slx x0 – it sleeps until data arrives via a port like x0. If you try to read them without slx, and on the other hand the microcontroller falls asleep, it will “freeze”. Until I knew this trick, I had to pervert it.

By the way, you can also use one bus for several microcontrollers. This works well in the “many transmitters – one receiver” mode and badly for several receivers (at least, I haven't figured out how to do it well).

Nandgame and Turing Complete

I didn't have the right subjects at the institute, I knew nothing about Verilog, VHDL, or microcircuit design. I wanted to figure them out for years, but I kept putting it off. In addition, I heard that students studying in the right profile can make their own processor as a diploma, and it seemed to me that it was still difficult.

I read how Habr user haqreu assembles a ternary computer and it seemed like magic.

So, thanks to these two games, I began to understand something. There are conventions, simplifications in the games, but everything is clear and the progression of levels carefully leads the player from the basics right up to the creation of the processor.

The initial levels of both games are similar (like adding two bits, three bits, operations for two bytes, etc.), then the difficulty increases, at some point you get a processor, and then you need to program for it.

The differences start somewhere at the middle levels, in nandgame you get a too simple processor with a couple of registers, and writing code for it is painful. In the end, the feelings are mixed – I finished the game, but something was missing.

Among the advantages of nandgame I would point out that it has a very interesting ALU design and a command system tied to it.

In turing complete the topic is covered in more detail. In it, as we progress, we first assemble some simple processor, but, by the way, already with six registers and relatively convenient. And then we start all over again and assemble a more advanced processor with a different command system and more interesting capabilities. The command system can be called redundant, but it is very convenient to expand it.

In addition, somewhere in the middle of the game a new feature appears – the game starts showing component delays, and you can bother and try to speed up the slowest path in the chip. In addition, you can go back to old levels and try to speed up basic components – then all chips that use them will also become faster. For example, I was able to speed up the addition of two bytes three times (up to 22, if anyone wants to repeat).

As you progress through the game, the instructions on what to add to the processor become less and less specific, and then disappear altogether.

For example, the game doesn't tell you how to make RAM. I first came up with separate commands for reading from memory and only then figured out that you can create another “sort of register” so that reading or writing to it would occur in a RAM cell at the address of some other register.

One of the next levels is to write a program to perform integer division and find the remainder.

The processor couldn't divide – to solve the problem I could write some code for division, or I could add a new instruction to the processor. Personally, I wanted to make an instruction, and for this – a block that accepts a pair of numbers at the input and sends the quotient and the remainder to the output.

The final processor had something like 5600 transistors, 2700 of which were for the division module. For comparison, PDP-8 there were only 519 transistors. That is, the circuit of my processor turned out to be ten times larger. Probably, in the future I will try to assemble the simplest processor in the game or repeat some existing one.

The game has 16, 32 and 64-bit components, I think they are specifically for players to experiment with – an eight-bit processor is enough to complete the game.

My processor:

At the top left is ROM and RAM memory, at the top right are registers and reading and writing to them, at the bottom right is ALU.

The small piece to the right of the ALU is stack operations – push, pop, call and ret, which are convenient for making function calls. The stack uses its own memory separate from RAM, it is hidden inside a custom component and is not visible.

P.S. I was inspired to write this article by a great article: Creating a CPU from Scratch for Dummies. In the comments to it, I learned about Turing Complete and dropped out of life for a few days. I recommend reading it too!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *