Avant, or a Look Back at My First Game Written in Assembly

2026-07-11

While I don't program in Assembly on a daily basis - or ever really - I do think doing a major project in the language was essential to me understanding Computing as a science.

Almost ten years ago now, for a class, we were given the task of writing a game. The requirements were essentially non-existant: it could be about anything, in any language, with any functionality. The only real requirement being that we documented what we did. The overarching goal of the project was to introduce us to project management when it came to software: how to allocate resources, how to determine functional requirements, how to wireframe, flowchart, and map out what a given project would look like.

I saw the project, and its lack of structure, as an opportunity to push the edges and see what I could do. At the time I was deep into retro computing (Your ZX Spectrums, Commodores, Gameboys, etc.). I had also just finished reading Charles Petzolds 'Code'. It was then that a dangerous combination emerged in the form of "I could do that." I wondered if I could put together some kind of retro-computer game of my own. I wondered if I could make it period-accurate. I wondered if I could do it in Assembly.

At this stage I had never written a line of Assembly in my life, didn't yet know software like DOSBOX existed, or whether such the ideas I had in mind were even feasible with my skill level. But, with six months until the deadline I decided to give it a go. The project then subsequently consumed the next six months of my life. I don't think I'd ever thrown myself so fully into a project before Avant. Naturally, with the complete freedom to program whatever we wanted, I went somewhat overboard. I now even have an original x86 refernce manual from Intel sitting proudly on my shelf.

The menu of the game featuring an ASCII-art dragon wrapped around the 'Avant' text.

Avant, as it would come to be called, is a randomised first-person dungeon crawler where our unamed hero must traverse dank caverns and passages, battling monsters in their quest to find the Sword of Destiny.

As some highlights, the game features:

As I would come to learn during development, the 'fun' thing with Assembly is that not only must everything be done manually, it also has to be done from scratch. There's no pre-made functions coming to save you: if you need something, you write it yourself. For this project that meant writing things like my own little random number generator, more efficient ways to print graphics to the screen, ways of saving and loading your progress etc. While at times tedious, nothing will make you appreciate the efficiencys of other languages more.

To give you a flavour of what writing a program in Assembly looks like, let's start with the opening screen of the game where the user can select what video card they have installed:

CardSelectionLoop: ; allow the user to select their video card mov ah, 00h ;wait for a keypress (no echo) int 16h ; initiate above cmp al, '1' ; was 1 pressed? je SetNoCardCall ; if yes then perform No card setup cmp al, '2' ; was 2 pressed? je SetCGACall ; if yes then perform CGA/EGE card setup cmp al, '3' ; was 3 pressed? je SetVGACall ; if yes then peform VGA card setup cmp ah, 1d ; was ESC pressed? je ExitProcessCall ; if yes then exit the program jmp CardSelectionLoop ; if no corresponding key is pressed then restart the loop

First, I define a label indicating what this section of code does (CardSelectionLoop). Then I call an interrupt to wait for the user to press a key (Selecting their video card). Once that comes in, I check (cmp) the value against the options available. If the user pressed a relevant value I jump (je or jump equals if the comparison above is true) to the relevant function to handle setting up that video card (SetNoCardCall, SetCGACall, SetVGACall).

As another example, the below will fetch and print the players current health to the screen:

mov dl, 31 ; X coordinate mov dh, 17 ; Y coordinate mov ah, 02h ; set cursor position int 10h ; initiate above mov dx, OFFSET PlayerHealthDISPLAY ; fetch string from PlayerHealthDISPLAY mov ah, 09h ; print string to display int 21h ; initiate above

I start by specifying the cursor position on screen which is stored in the dl and dh registers, specify I want to move the cursor with the relevant interrupt (02 in dl) and then call that interrupt (int 10h). Once the cursor is in the correct position, I fetch the variable containing the players health (PlayerHealthDISPLAY) and print it to the screen (ah containing 09) at that cursor position.

Hopefully from just these two examples you can appreciate how crucial it is to comment your code in Assembly. If you get sloppy and start writing even small sections without any annotation, you're going to be spending a lot of time checking interrupt lists later on to remember what does what.

A first-person view of a tunnel with an opening to the left. Depicted in ASCII art.

As development progressed, working with Assembly took on a different feel from anything else I've experience working with other languages. Because you're so low-level with the machine, it's possible to know the exact state of the machine, down to the CPU, at any moment the game was running; something it's easy to lose track of in higher-level languages. In a strange way, this almost made troubleshooting bugs easier. If I know a register should contain a certain value at a certain point in the program and it doesn't, I'm on my way towards tracking down what's causing the issue.

This extended to adding in new features as well. Because everything is very atomic (Essentially just moving numbers around), I found it easier to just throw in new functionality as the need arose - as long as I've PUSHed and POPped my way back to the way things were before the new feature then I'm all good to go. Obviously, much of this sort of thing is handled automatically in other languages but taking the time to do it manually did go some way in making it concrete in being mindful of what the hardware is doing.

A combat screen showing an orc enemy with a range of combat options and stats in the lower half of the screen.

Many an introductory CS class will talk about Assembly as the type of thing the CPU works with to perform operations below the programming languages you'll typically be working with. That level of understanding is enough for most people but there's a key difference between understanding a concept because you heard it in a talk or read it in a textbook and really getting that hands on experience.

For me, actually building a decently-sized project in Assembly was extremely useful in solidifying many of the concepts and practices that tend to get left as an after thought: managing memory efficiently, writing code concisely, even just formatting code in a way that's easy to come back to. All of these are skills I don't think I would have taken as seriously had it not been for this project.

Do I reccomend that you drop everything and start working exclusively in Assembly? No. Absolutely not. I cannot stress enough - no. Even for someone so keen on minimal computing as me I have my limits. But I think everyone should at least try implement something non-trivial in Assembly even just once. It gives a refreshing perspective on how far computing has come and the many, many convenencies that we now enjoy during development.