Donkey Kong Part Two: Filling out the Memory Map

In the last article we started fleshing out the memory map for Donkey Kong. We learned how to identify sections of ROM versus sections of RAM and began poking around with general game variables in memory. We can do a lot armed with this knowledge, but occasionally we’re going to have to dig a bit deeper and further flesh out a game’s memory map to learn more about it.

In this article we’ll focus our efforts towards understanding more of the memory map. We’ll be taking a look at variables relating to the game’s entities as well as the frame buffer.

The Frame Buffer

Let’s start with the frame buffer since that’s probably easier to identify compared to variables responsible for controlling game entities. Before we start rooting through the memory view in MAME looking for the frame buffer let’s quickly recap what a frame buffer is as well as what it does.

When video games draw to our system’s screen all of that graphical data is stored in a section of memory known as the frame buffer. By storing image data in memory rather than just drawing pixels as they are needed it’s possible to achieve a more stable image and the programmers don’t need to concern themselves with display timing issues. It also tends to be pretty efficient since games typically do not need to refresh the entire frame buffer in order to change the image on the screen.

So what are we looking for in a frame buffer? Generally speaking there are a few things that we can look for to determine the boundaries of the frame buffer. Contents of the frame buffer don’t change radically frame by frame, however they will look pretty different between screen transitions.

For example the background image in Donkey Kong during a barrel stage doesn’t really change until the level ends. When the level does end, however, the contents of the background change pretty dramatically. Knowing this we can examine memory using the MAME debugger’s memory view and look for large areas of memory that basically only change during screen transitions.

One good way to regularly observe screen transitions in memory is to put the game into attract mode and scroll through the memory view in the MAME debugger. Once again we’re looking for large, contiguous areas of memory that pretty much only change with the screen transitions during attract mode.

We already know from the work we did in the previous article that the memory range of 0x0000-0x3FFF is an area of read-only memory so we can skip right past these addresses and continue scrolling onward. Around 0x6000-0x712F we see lots of patches of small areas of memory that appear to be constantly changing so this area is likely not of interest to us right now. We hit something interesting around 0x7400, however:


Sandwiched between two large slices of null (0x00) bytes we can see a large chunk of memory set to specific values that apparently only change when the screen does. Since we’ve exhausted most of the used memory space in our search this section of memory stands a pretty good chance of being the frame buffer that we’re looking for.

Supposing that this region is our frame buffer is all well and good but at the end of the day we’re going to need some proof. How exactly can we go about proving that this is our frame buffer? The simplest way that I can think of is the same way we tested our credits variable in the previous article: we overwrite the region of memory with a predetermined value and observe the result.

You see most games represent images in frame buffers as a set of tiles. Often times these tiles are pieces of the game’s background and the game renders the sprites on top of tiles. These tiles are represented by byte values in RAM so changing the values in the frame buffer should result in a change of the background image.

So let’s go ahead and start overwriting this section with null bytes and see what happens. First we’ll halt execution of the game by issuing the ‘gv’ command, which will cause the debugger to halt the game after the next frame has finished writing to the screen. Next we’ll go ahead and write our null bytes like so:


As you can see I’ve filled roughly half of the memory region with null bytes. We don’t have to look very far to see these changes in action. If we take a look at the screen we’ll notice that it looks like the following:


It looks like roughly half of the screen has been covered in zeroes, which is in line with what we would expect. As far as proof goes I’d say that this is pretty definitive. Based on what we can see here we can be pretty sure that the frame buffer spans memory addresses 0x7400-0x7800.

We’ll come back to messing around with the frame buffer in later articles. If you’re curious to find out all the tile values or how the screen is laid out in memory I encourage you to play around with setting various values and observing the output on the screen.

Game entity variables

Game entity variables are probably the most difficult variables to pinpoint. For one thing changes to game entity variables might not be immediately apparent like our frame buffer earlier or those changes may not have an obvious effect on the entity being edited. For another thing finding individual game entity variables is sort of like finding a needle in a haystack since you’re generally looking for a handful of bytes out of thousands. So what’s a reverse engineer to do?

Well, I’m going to be honest with you guys here, this is where a lot of the drudgery of reverse engineering arcade games comes into play. Success in this area generally boils down to the will to persevere in terms of toggling byte values in memory over and over again. I know that doesn’t sound terribly exciting but that’s generally how it is.

Fortunately we’re not totally in trouble in terms of lack of techniques when searching for game entity variables. One of the easier things that we can do is scan the memory map like we did for the frame buffer but look for repeating patterns in memory. The basic idea here is that similar game entities or objects in a game will likewise be structured similarly. It’s also not unusual to see game entities organized contiguously in memory so repeating patterns in memory are a good place to start.

Instead of simulating lots of work I’m just going to take you guys directly to an area of memory I’ve studied before. We’ll leave the drudgery for another day and jump straight into variables relating to barrels.

If you stare at memory long enough during the attract mode for Donkey Kong you might see this section of memory jump out at you:


There’s a little bit of variation here between these different 32 byte chunks of memory that are highlighted, but the general structure of these chunks appear to be similar. The first byte of each of these structures seems like a logical place to start messing around with so let’s go ahead and do that.

You may have noticed that the first byte of these structures appears to toggle between the values 0x00 and 0x01 during the game’s attract mode. Since these memory locations start with the value 0x00 let’s detect when it initially toggles to 0x01 and then set it to 0x00 to see what happens.

First let’s take a look at what values that first byte is actually set to during the attract mode. We’ll be using the first byte of the first offset at 0x6700. To monitor every write to this address we’ll set a watchpoint for it with the following command:

wpset 0x6700, 1, w

This command sets a watchpoint for address 0x6700 that is one byte wide and only trips on write operations at this address. Let’s continue through the attract mode and let the watchpoint do the heavy lifting for us. If you haven’t done so already resume the game with the ‘go’ command. After the attract mode starts you should notice a watchpoint is tripped that looks like the following:


Here we can see that 0x6700 has been written to when the code at address 0xF68 was executed and that the value of 0x6700 was set to 0x00. Let’s continue through the rest of the attract mode with the ‘go’ command and observe the rest of the values this byte is set to:


It looks like the byte at address 0x6700 is actually set to three values: 0x00, 0x02, and 0x01 in that order. After viewing the first attract mode in its entirety we can already see this pattern emerges repeatedly. If you stared at the game while observing when the watchpoints trigger you probably noticed that these values corresponded with certain events occurring within the attract mode.

For instance you may have noticed that when 0x6700 was set to 0x02 that it appears that Donkey Kong is attempting to throw a barrel. You may also have noticed that not long after the barrel is set to 0x01 it appears the barrel is rolling. Lastly you may have noticed that when 0x6700 is set back to zero the barrel appears to despawn after it reaches the oil barrel at the end of the level.

Alright, so what happens when we go from the value 0x02 directly to 0x00? Let’s change the value in our memory view at the appropriate watchpoint and find out:


First we wait for the watchpoint where we transition from the value 0x02 and 0x01. We’re basically going to overwrite that value to be 0x00 which effectively will skip that 0x01 value. Let’s go ahead and advance the game one frame with the ‘gv’ command. You should see the following on the screen:


Alright, here we can see donkey kong attempting to roll the barrel. Let’s advance another frame:


Here we see that the barrel appears to be deployed. Let’s advance the game several frames just to be sure that’s the case:


We can see that the barrel Donkey Kong just threw eventually disappears and he grabs for another barrel! So what’s happening here?

This behavior is a pretty strong indicator that the first byte of each of these regions of memory we’re looking at is some kind of status indicator. A status of 0x02 apparently means that Donkey Kong is trying to fetch that particular barrel to throw it and 0x01 and 0x00 appear to control whether the barrel is enabled or disabled.

Lots of different entity and general game variables can be discovered and manipulated like this using watchpoints and the memory address. We’ll certainly be examining more of both in future articles but for an added exercise I would recommend setting breakpoints on the rest of the first barrel’s variables and see what manipulating them does. Doing so should offer a lot of insight into variables such as barrel type and which direction the barrel is traveling.

What’s Next?

So at this point we’ve seen pretty much all the examples we need to see in order to start discovering different regions of memory and tinkering around with their values. We also learned a few tricks for locating certain types of variables even if they may be a fair bit of work. The next steps we’ll take with reverse engineering Donkey Kong will focus on relying more on cutting down on this work by examining disassembled code for clues into how memory is used.

In the next article we’ll be using watchpoints to view related disassembled code in order to give us clues as to how memory is used.

Special Thanks

I’d like to take a moment to thank my friend Ray who was generous enough to send me a pretty difficult to find board for my Sinistar restoration that I’ve been working on recently. Thanks Ray and I apologize for making you wait so long for this article!

Donkey Kong Part One: Building the Memory Map

As promised with this post we’re going to begin diving into actual games rather than code samples and the first game we’ll be exploring in depth is the Nintendo classic Donkey Kong. Throughout this series each post will focus on a different aspect of reverse engineering and the general process of figuring out how these games work.

The reverse engineering process in a nutshell

In a very abstract kind of sense the act of reverse engineering is the process of us asking ourselves questions about a piece of technology and testing them to find the answers to those questions. These answer added up begin to form a bigger whole in terms of our understanding of how that piece of technology works.

Remembering back to the microprocessor fundamentals articles you will recall that in order for a microprocessor to do anything really useful in the long term with data that memory is required. Memory provides the microprocessor not only a static space to store game code (ROM) but also temporary scratch space for variables and other game data (RAM). Since the game will use different RAM address locations for various pieces of game functionality  it’s important to create a memory map, or list of addresses of interest, when examining a game.

Once a memory map is created it’s possible to monitor reads and writes to various addresses that we’re concerned with to identify portions of code associated with various game funcationality. After identifying these code locations it’s then possible to learn how these different pieces of code function using the disassembly view of our debugger.

Once we’ve learned how a piece of code works we can then work on modifying that code to perform some desired effect. Maybe that effect is not subtracting a life on death or automatically coining up a machine on start or something of that nature. These modifications are accomplished by using a hex editor and translating modified code into its hexadecimal notation. Basically we perform the work of the assembler and patch the binary like a patching program would using a hex editor.

So to sum it up in quick bullet points the general process of reverse engineering a game goes as follows:

  • Establish a memory map
  • Find code associated with various functionality; learn how it works
  • Modify desired portions of code to work differently
A brief word on MAME source code

It’s worth mentioning that the MAME source code can be a convenient source of information regarding game hardware and memory maps. Often times MAME driver authors will even detail a game’s memory map in the comments of the driver source code itself.

While I believe that this is a valid strategy for obtaining a game’s memory map this tutorial series will focus on trying to flesh out our own memory maps as we work with these games. It may take more time to discover address locations on our own but I believe that in the end we will learn more about these games as a result of doing our own exploration. That said there may be times where we dig into MAME driver source for memory locations and by no means are you discouraged from doing so in your own work.

What are we looking for in a memory map?

The first thing worth noting about developing a memory map is that the memory map is not something that’s usually drawn up in full before moving on to examining code. Since the purpose of each address location won’t be immediately known to us as we set out to figure out different parts of the game the memory map will end up being a living document of sorts where we fill the map in as we learn new facts about the game.

Often times when building memory map we’ll find variables in memory that are logically grouped together or serve a similar purpose. For this reason it’s helpful to think of building a memory map as a general process of defining regions of memory where these similar variables are located and then later determining their individual purposes. General regions of memory that we are concerned about when building a memory map are as follows:

  • ROM
  • RAM
  • General game variables
  • Game entity variables
  • Frame buffer
  • Device input/output

There are probably more examples that could be listed, however many of the things we will be concerned with when modifying game ROMs will fall into one of the categories listed above.


One of the first distinctions that should be made is between “ROM space” and “RAM space”. Knowing where ROM ends and RAM begins is important since we’ll want to focus a lot of our early efforts on the RAM portion of memory.

While it’s possible that ROM and RAM can be mapped to anywhere in memory space the designer desires we can usually make a few assumptions about where ROM memory begins based on microprocessor architecture. For instance if you’ll recall the Z80 microprocessor begins fetching and executing instructions at memory address 0x0000. Since we know that the ROMs contain game code and the microprocessor expects game code to be located at 0x0000 it is reasonable to assume that ROM data will be located at memory address 0x0000. Typically in a Z80 based system RAM data will start in the later memory addresses.

General game variables

General game variables, like the name implies, are variables that have general effect over the operation of a game or its gameplay. These variables tend to be global in nature and could range from credits left in the machine to the current player score.

It’s worth noting that usually for reasons of programmer sanity these general game variables tend to be located near each other. Even though development teams on these games were smaller in the ’80s multiple people still had to be able to work on the same machine and once variables were assigned memory addresses and used it could be a really bad decision to change that later on. For instance changing the location of the player lives variable and forgetting to update every portion of code that used the old location could have unintended consequences and lead to costly and time consuming bugs. Since it’s easier to just allocate a decent chunk of memory to general global variables it’s not uncommon to see this practice in the wild.

Game entity variables

Without trying to sound like a ridiculous tautology game entity variables are variables which describe various objects in the game. For instance every game incorporates the idea of a player entity into its gameplay or in the case of Donkey Kong we have entities not only in the form of the player but in the form of barrels, fire balls, Donkey Kong, etc. The location of the player or barrels on the screen or the speed at which they’re traveling would be considered game entity variables.

For the same reasons of programmer sanity variables for a particular entity are usually grouped together and similar entities are represented by the same number of variables. For instance not only do the barrels and fire balls in Donkey Kong all have a unique area in memory where they are represented and use the same amount of memory for each entity but they are also located sequentially in terms of memory address. We can use this knowledge to spot patterns in memory that lead us to find where entity variables are stored.

Frame Buffer

When drawing things to a screen we need to reserve a section of memory as a space where we can store the data that we want to draw. This area of memory where we store video data is known as the frame buffer and it is common to every game that you will reverse engineer. In order to save on the actual amount of RAM used most games will use their frame buffer in a tile-based rendering system where tile numbers are stored in bytes of RAM that correspond to pixel data stored in sprite ROMs on the board. In the case of Donkey Kong we’ll see how the frame buffer is used to construct the backgrounds that the game entity sprites are projected onto.

Device I/O

A common practice in computer design is to allow external hardware to interface with the microprocessor’s address and data bus. This is done so that devices can be read from or written to by addressing memory location and is known as memory mapped input/output. It’s very common to see control panel and coin mechanism hardware handled using memory mapped I/O even in systems where the microprocessor has separate facilities for dealing with I/O such as Z80 ports.

Let’s get started

That’s enough general theory for now; let’s dive into Donkey Kong and begin building our memory map. We’ll start out by determining ROM sections of memory versus RAM sections and locating some general game variables.

The first thing we’ll do is launch Donkey Kong in MAME in debug mode. The command to do this is as follows:

After launching this command you should see a screen that looks like this:



Now we’ve got MAME running with the debugger window displayed. We’re going to be doing a lot of work with the memory view in the MAME debugger when building a memory map so let’s go ahead and select Debug -> New Memory Window. After making this selection something like the following window should appear:


Determining ROM and RAM regions

The first thing that’s generally helpful when building a memory map is getting an idea of what ranges of memory you can and cannot write to. This means finding the ranges of ROM versus RAM so that we know where the code lives and the possible range of addresses for our game variables.

We could do some calculations regarding file size and number of EPROMs located on the game board to find the ROM address range however the memory view in MAME proves to be the most useful tool for the task. Provided you know a few simple tricks determining the address space for ROM should be a simple task.

When non-volatile RAM is powered on it starts in a cleared state. This means that before any values are written to memory that each byte of that memory is the same value. For most non-volatile RAM this value would be 0x00.

Since the Z80 microprocessor begins loading code at memory address 0x0000 and the system RAM will be initialized to all zeroes we can infer that every byte between 0x0000 and this sequence of zeroes is our ROM area. All we’ll need to do is scroll through the ‘maincpu’ memory in the memory view window to determine the ROM address range.

Scrolling through our memory map window we’ll eventually come across the following:


Here we see that a large sequence of repeating 0x00 bytes starts at memory offset 0x4000 and continues until the end of the memory view. Given this information it’s reasonable to infer that the ROM region of our memory map spans memory addresses 0x0000-0x3FFF and that every address beyond that is potentially a RAM address.

As a fun aside we can also see an easter egg contained within this ROM: a “secret” message from the original developer of the game. The message appears to be garbled in the US version 1 ROM dump, but the message originally read:


You see Donkey Kong was not actually developed by Nintendo but a company known as Ikegami Tsushinki, a company known these days for manufacturing commercial grade camera equipment. It would stand to reason that Ikegami programmers included their message for would-be reverse engineers of the day, though for what purpose exactly we can’t be certain.

Finding general game variables

Now that we’re through with the first and easiest part of building a memory map by defining our ROM and RAM regions we can move on to locating general game variables. General game variables are good to try and work out second due to the fact that the values of those variables are likely to change less over time or tend to stand out in the memory view.

Since the number of credits remaining tends to be pretty easy to spot let’s start with that first. We’re going to be paying attention to memory addresses 0x4000 through 0xFFFF which is the region we previously defined as being potential RAM. Entering the ‘go’ command into the debugger and scrolling through memory until we start seeing data lands us around memory address 0x6000. We should see something like the following around 0x6000:


One thing you may also notice looking at this section of memory is that not only is there data starting around 0x6000, but the data that’s there isn’t changing for the most part. These are some pretty good indicators that the values we’re looking at serve as global variables of some nature. Even if that ends up not being the case this is pretty much as good of a place to start looking as any since everything between 0x0000 and 0x5FFF is either ROM or uninitialized RAM.

Knowing where to start looking is all well and good, but how should we start testing for where the credits variable is? Well, the easiest way I can think of to test this is to coin the game up while looking at the memory view. It stands to reason that wherever this variable’s memory address is that its value will start out as zero, or 0x00, and continue to increment by one every time the game is coined up. So all we should need to do to test for the location of the credits variable is look for a value the increases by one in the memory view every time we press the credit button. Let’s start coining up the machine by pressing the coin up key, which is 5 assuming you have the default control bindings.

Well as it turns out we don’t have to look very far to find a potential match. Just one byte in at 0x6001 we can see a value that mirrors the current number of credits and increments when we coin the game up and it would appear that we have found our credits variable. Let’s test this further by overwriting the value at 0x6001 and seeing if it changes the number of credits left in game. To do this we’ll highlight the value at location 0x6001 and type the value ’99’ in its place.


As we can see setting the value at address 0x6001 to 0x99 results in 99 credits remaining in the game effectively proving that memory address 0x6001 is the address for the credits variable. We can actually use this trick of setting values in memory and looking for their effect in game to test all kinds of variables.

Where do we go from here?

This article pretty much only begins to scratch the surface of our exploration into Donkey Kong and games in general. We began by finding our basic regions of memory as the microprocessor sees them and identifying a basic game variable using the memory view. In the next article we’ll continue to identify and examine different regions of memory in Donkey Kong and future articles will focus on monitoring memory addresses to step through code. Once we have these basics down we can perform this regimen of identifying memory addresses and monitoring them to get a better picture of how the game works and begin making our modifications to it.

Introducing the MAME Debugger

When it comes to disassembling and debugging arcade games on a PC it helps to have an emulation layer. Having an emulation of the target system and allowing the user to debug that target system is really what we’re after since strictly examining the program’s disassembled code quickly becomes a cumbersome process.

When it comes to arcade game emulation the name on most everyone’s mind is MAME, and for good reason. MAME supports many different arcade machine hardware architectures and in most cases the accuracy of emulation will be more than adequate for our needs. MAME also provides an interactive debugger right out of the box and this debugger will serve as the basis for our debugging and testing platform.

Launching the MAME Debugger

Launching MAME with the debugger only requires that the user stipulate an additional flag when launching MAME. Most of you are probably familiar with launching MAME from the command line, but for those of you unfamiliar the general syntax goes something like:

To launch the MAME with the debugger enabled simply add the -debug argument flag like so:

Provided this syntax is followed and MAME can find your ROM you should be greeted by the following on your screen:


The Debugger UI At a Glance

Since we’ll need to be familiar with the debugger’s user interface let’s take the time to examine it now. Since there’s quite a bit of information on this interface in a relatively small amount of space I’ve gone ahead and highlighted as well as labeled the major areas that we’ll be working with.


The disassembly view gives us a look into the disassembled game code and can be thought of as a disassembler that is built directly into our debugger. On the far left of this view we have the memory addresses of our instructions and to the right of that are the accompanying human readable instructions. On the far right of this view we have the hexadecimal representation of the instructions and their operands, which becomes particularly useful when searching for a particular sequence of bytes when we want to modify a ROM.

The register view gives us a glimpse into the contents of the microprocessor’s various internal registers at any given point in time and also allows us to modify the contents of any given register. This view is especially handy when examining reads and writes to and from memory addresses since constant values are generally stored in a register and the contents of the register are transferred to memory in a subsequent operation. Registers in the register view appear in their valid register pair combinations rather than individually.

The command view is where we issue commands to the debugger and view the results of commands as well as triggered events such as breakpoints. A command line interface at the bottom of this view allows us to enter debugger commands and we can review the results of these commands in the space above it.

Since two out of our three views are essentially displays that we interact with to a lesser degree we’ll be focusing mainly on the use of the command view in this article. In particular we’ll be focusing on stepping, breakpoints, watchpoints, and editing the contents of RAM.


You may have noticed when you launched MAME with the debugger enabled that the game did not automatically boot. This is due to the debugger setting a sort of default one-off breakpoint at reset that triggers immediately before the first instruction is executed at 0x0000 and prevents the game from booting. If we want to continue executing subsequent instructions the debugger expects that we will issue it one of multiple stepping commands. In general these commands will be single or multiple steps and continuing execution until some event causes the debugger to halt again.


The simplest stepping command we can issue to the command line is the step command. Issuing step by itself will cause the currently highlighted instruction in the disassembly view. If we want to execute multiple steps with one command we simply supply the number of steps we wish to execute as an argument to the step command like so:

In this example our command would execute four steps in sequence and halt execution.


The over command operates like the step command except the instruction at the current address contained in the PC register isn’t executed and PC is advanced to the address of the next instruction. Similar to the step command issuing the over command by itself will skip one instruction and supplying the number of skips as an argument will cause multiple over commands to be executed in sequence.

This example command would cause the over command to be executed 9,000 times in sequence which would skip 9,000 instructions.


The out command is intended to allow the microprocessor to resume execution and halt when the end of a function is reached. In the context of Z80 assembly this basically means waiting until a ret instruction is executed and then halting execution on the next instruction. Since functions in Z80 assembly are implemented using call and ret instructions the out command is particularly useful for when we want to exit from functions that we don’t wish to spend any time stepping through. The out command does not accept any arguments and halts after the first ret is executed.


The go command allows program execution to continue either indefinitely or until a particular address is reached. Issuing the go command by itself continues program execution indefinitely and it also accepts a memory address as an optional argument to set a temporary one-off breakpoint to halt execution. This command is often useful for preventing boredom while debugging by allowing us to skip code we’ve already reverse engineered.

This example command would cause the program to continue executing until the address 0x1000 was reached at which point it would halt execution.


The gvblank command allows program execution to continue until the next vblank, or vsync, signal is generated. In many games the monitor’s vblank signal is connected to the interrupt pin of the game’s microprocessor. Since this signal is generated every time a frame is rendered by the monitor it effectively allows the monitor to point code execution back to the interrupt vector where the code for the game’s main loop is conveniently located every frame. This command becomes really useful when you want to reverse engineer a particular game’s main loop. The gvblank command does not accept any optional arguments.


We learned in the last article that breakpoints are a way of halting code execution so that we can control the flow of code to observe its behavior. Using breakpoints allow us to define memory addresses that when reached will halt execution and allow us to step through code near those addresses.


Breakpoints are set in the MAME debugger using the bpset command. The bpset command takes at least one argument which is the address to set for the breakpoint. Additional arguments can be supplied to bpset to specify conditional breakpoints, however we’ll be focusing on the simplest usage for now.

This example command would set a breakpoint at memory address 0x2A00 which would result in code execution halting if or when that address is reached.


When breakpoints are created they are automatically assigned a number by the debugger as a means of identifying and selecting various breakpoints. The bplist command allows us to view a list of available breakpoints and is often helpful when we need to selectively enable, disable, or clear a single breakpoint. The bplist instruction doesn’t accept any arguments.


Sometimes when we’re working with multiple breakpoints we’ll want to disable certain breakpoints without losing them completely and the bpdisable command allows us to do just that. The bpdisable command takes the number of the breakpoint to disable as its only required argument.

This example command would disable the breakpoint with ID number 3.


As the name might imply the bpenable command allows us to enable a disabled breakpoint. The bpenable command accepts the ID number of a disabled breakpoint as an optional argument or enables all breakpoints when issued by itself.

This example argument would enable the breakpoint with ID number 3.


The bpclear command allows us to delete one or more breakpoints. Issuing a bpclear command by itself and accepts the ID number of an available breakpoint as an optional argument which clears that single breakpoint.

This example argument would result in the debugger clearing the breakpoint with ID number 3.


We learned in the previous article that watchpoints are effectively breakpoints that operate on memory access rather than the address held in the PC register and that they are particularly useful for finding sections of code that are responsible for dealing with those memory addresses. Watchpoint commands operate very similarly to breakpoint commands in the MAME debugger with the exception that a few of the watchpoint commands accept more optional arguments as input.


The biggest difference between setting a watchpoint and setting a breakpoint in the MAME debugger is that watchpoints require at least three arguments. The wpset command requires three comma-separated arguments in the following order: the memory address to watch for access, the length of the data in bytes to watch for, and the type of memory access. The access type must be read (r), write (w), or read-write (rw).

This example command would result in the debugger setting a watchpoint for read or write access to memory address 0x22A0.


The wplist command lists all available watchpoints and operates exactly the same as the bplist command in that it does not accept any additional arguments as input. Watchpoints are also assigned ID numbers exactly like breakpoints.


The wpdisable command disables an enabled watchpoint. Much like the bpdisable command issuing wpdisable by itself disables all watchpoints globally and supplying an optional watchpoint ID number will disable only that single watchpoint.

This example command would result in the debugger disabling the watchpoint with the ID number 2.


The wpenable command enables a disabled watchpoint. Issuing wpenable by itself will result in all available watchpoints to be enabled and supplying an optional ID argument allows us to enable individual watchpoints.

This example command would result in the debugger enabling the watchpoint with the ID number 2.


The wpclear command allows us to clear watchpoints either globally or individually. As you might have guessed issuing wpclear by itself will clear all available watchpoints and supplying an optional ID as an argument will clear an individual watchpoint.

This example command would result in the debugger enabling the watchpoint with the ID number 2.

Memory view and editing RAM

One piece of debugger functionality that I wanted to touch on briefly in this article is the memory view. The memory view allows us to read the contents of both RAM and ROM and allows us to edit the contents of a game’s RAM which is extremely helpful for determining what variables in memory do. If we select Debug->New Memory Window a new memory window should appear similar to the following:


The number in the upper left of the window is the starting address offset for the memory view and the drop-down list next to it allows us to select various devices from the available hardware and view their available memory space. Everything below that are memory offsets and the hexadecimal/ASCII representations of the data at those offsets.

If we’re in an area of memory that we know is RAM and we would like to edit a variable we can simply highlight the hexadecimal value listed at a given offset with our mouse and enter a new value with the keyboard. I personally use this technique a lot to figure out what certain variables do and to find areas of memory that act as the game’s frame buffer.

Wrapping Up

By now you should have a basic familiarity with the MAME debugger and its use. There are certainly more advanced commands for this debugger, but we’ll be covering those commands as we’re actually reverse engineering games. Speaking of actually reverse engineering games the next article will mark the beginning of our games analysis series of articles starting with Donkey Kong! In these articles we will be applying everything learned so far to actual games in the wild and picking up reverse engineering tricks along the way!

Reverse Engineering: Disassemblers and Debuggers

So at this point in our series of articles you should be fairly familiar with Z80 assembly, and while we won’t be writing our own programs from scratch this information will be invaluable to any efforts at reverse engineering software running on the Z80 microprocessor. This article will focus on building basic familiarization with reverse engineering tools and terminology commonly associated with them.

What is reverse engineering?

In simple terms reverse engineering is the process of deducing how a particular piece of technology works through careful analysis of the technology while it works. In most fields the goal of a particular reverse engineering effort is typically to clone or modify the original technology.

In software terms reverse engineering typically involves analyzing the effect that the software has on the microprocessor to infer what the code is doing at any particular point during execution. While many utilities exist to ease reverse engineering efforts this process is most commonly facilitated through the use of tools known as disassemblers and debuggers.


A disassembler is a tool that takes an assembled executable binary and converts instruction opcodes and operands back into a human readable format. This essentially means that we can take assembled Z80 code and turn it back into Z80 assembly source code, which is effectively the opposite process the assembler uses to produce an executable binary from source code.

Disassemblers are essential to software reverse engineering since we need to know how the code of a particular system is written in order to work with it. For this reason the first step in reverse engineering software often involves obtaining disassembled instructions from the binary.

Using a disassembler is typically pretty straight forward. Often times the only required argument is the file to disassemble with optional starting and ending address offset arguments to limit disassembly to a specific portion of the binary if desired. Anything beyond that is usually specific to the microprocessor architecture or the particular disassembler being used, but often times simply obtaining a disassembly of the whole file is all we’re looking for.

Here’s an example of disassembling some Z80 code using the z80dasm utility in linux:

This output is the first screen of Z80 disassembly for the game Donkey Kong. We specify the -a flag to tell z80dasm that we’d like it to print the address offsets for each instruction and we also supply the -g argument with the value of 0 to indicate that we would like these addresses to start from offset 0x0000. The only other argument needed is the name of the file to disassemble, which in our case is c_5et_g.bin. From there the disassembler will go through the entire file, parse the opcodes and operands, and print them out to the screen. For those wondering I will usually pipe the output of the disassembler to the less command in order to make the disassembly output easier to work with.

Aside from a few instructions that may look foreign to you the above disassembly should look pretty familiar. What you may have already noticed though is the total lack of labels and that jumps are either referring to specific memory addresses or offsets representing the amount of bytes they want to jump. When assembly code is put together by an assembler all of those labels for address offsets are tossed aside and the result is simply the location in memory for the label or variable. As a result variable names and labels will not be available to us when we go to reverse engineer software.


Being able to view disassembled code is a great step in the right direction, but often times simply being able to read the code isn’t enough. While we can make some great deductions about code from reading the disassembly things like stack manipulation and conditional branching become difficult to keep track of in our heads while reading code. What we really need is a tool that will keep track of these things for us and allow us to direct program flow so that we can examine the state of the microprocessor.

A debugger is a tool that allows us to control the execution of a program in order to examine the microprocessor at various stages of program execution. By halting the program at certain memory addresses we can know the state of the microprocessor at that time with absolute certainty and by controlling the amount of instructions executed it’s possible to follow conditional branches without the need for keeping track of them in your head.

Most debuggers also have the ability to change things like register values which makes it possible to do things like force code execution to take specific paths. This can be useful for determining what portions of code are intended to do by forcing the microprocessor to take the unintended code path and observing the behavior of the system. For instance one thing I like to do when reverse engineering arcade games is to figure out how to coin the game up without actually having done so. To do this I’ll find where a read operation occurs on the coin mechanism and force the microprocessor to take the unintended code paths rather than the intended ones, which usually results in a credit being added to the game.

It’s also worth noting that many debuggers also include disassembler functionality, which makes sense considering the disassembler is necessary to know the offsets for where you want to halt code execution.


A breakpoint allows us to specify an absolute memory address so that if or when the debugger reaches that address it halts execution. Most debuggers support the concept of both unconditional and conditional breakpoints. In the case of unconditional breakpoints the debugger will always stop when reaching the specified address and in the case of conditional the debugger will only stop if some user-defined condition has been met.


As the name implies watchpoints are a special type of breakpoint that allow the debugger to halt execution when read or write operations occur in a section of memory.  Rather than providing an absolute address to halt on the user provides the debugger with a memory address or range of addresses to watch for either read or write operations.

Watchpoints are particularly useful for when we want to find code associated with various functionality. For instance if we’re working on a game and we know that the amount of credits remaining in a system is held at memory location 0x8000 we can set a watchpoint for read and write operations on 0x8000 to find the areas of code that read from and write to that address. From there anytime the debugger halts we can assume with relative certainty that the code near where we halted is meant to do something with credits.


So we’ve covered two methods of controlling program flow with breakpoints and watchpoints, but sometimes we only want to execute a single instruction or batch of instructions before halting again. Controlling program execution in this way is referred to as stepping since the user is stepping through the program a per-instruction basis. The act of stepping one step at a time through a portion of a program is often referred to as single stepping.

Stepping is often very useful for determining what is actually occurring within a program during execution. Breakpoints or watchpoints can be used to locate sections of code dealing with some desired piece of functionality to debug and after the breakpoint or watchpoint has been hit it’s possible to step through the code line by line to determine how it works.

Taking our earlier remaining credits example if we wanted to determine how the code for subtracting a credit worked we could set a watchpoint for write operations on memory location 0x8000 and wait for the watchpoint to be hit. This watchpoint would get us in the general area we need to be to find the code responsible for subtracting the credit and we can single step through the rest of the code to find the exact line responsible for the subtraction.

Wrapping Up

By now you should be familiar with some basic concepts and terminology associated with disassembling and debugging programs. Don’t fret if it doesn’t make total sense yet since we’ll be getting plenty of hands on experience using a debugger during the course of this series.

Z80 Assembly Part 4: I/O and Interrupts

So at this point in this Z80 assembly series we’ve covered many of the concepts necessary for reading or writing fully functional and useful Z80 assembly programs. Everything we’ve covered has centered mainly around interactions between the microprocessor and memory however systems also generally require input from the user and other devices. For this reason our microprocessor supports I/O, or input/output instructions for data transfer between devices and interrupts for handling these input and output operations.


When the microprocessor grabs data from external hardware that data is known as input. Input in arcade games is typically present in the form of button presses and joystick positions and in computer systems input typically takes the form of key or mouse button presses.

We can instruct the Z80 to grab input from a device using the IN instruction, which allows the microprocessor to transfer one byte from external hardware into one of its registers. The IN instruction takes two arguments, the first being the register to store the input and the second being either an immediate value representing an address or register holding the value of the address. Addresses used in input and output operations are known as ports and the Z80 officially supports up to 256 addressable ports ranging from 0x00 to 0xFF.

Let’s suppose we’re working on a game for some fictitious gaming system. A common task we’ll want to do during a game’s main loop is to poll the user’s input in the form of button presses and joystick positions to update the player sprite. We’ll suppose for the sake of simplicity that the controls are wired to port zero (0x00). The code to poll input from the controller would look similar to the following:

Executing these two lines of instructions would cause the current state of the controller or control panel to be stored in the A register. After receiving the input into the A register we’d want to examine it and make an update to the player sprite. For the purposes of this exercise we’ll just say that the most significant bit, or bit 7, of the byte returned represents whether or not the joystick is currently in the left position. We’ll also say that in our game system a result of one in a bit indicates that a control is activated and that a result of zero means the control is not activated.

We can check individual bits of a byte using the BIT instruction, which takes two arguments. The first argument is the bit to check and the second argument is the register or memory location of the byte to check. The assembly code to do this might look something like:

Here we can see on line 3 we check the value of bit 7 of the A register, which holds the result of our input call from our controller hardware performed on lines 1 and 2. If the result is one, or not zero since there are only two options, then we jump to our code that moves the player sprite left. If the result is zero we would simply move on to whatever lines 5 and onward would happen to be, which would probably be more comparisons against the rest of the bits.


Sometimes the microprocessor needs to send data to an external device in order for that device to perform some type of operation. Data that is sent to external devices from the microprocessor is known as output.

Examples of output can span all sorts of interactions with various hardware on an arcade game. Common examples of these interactions would be output to audio hardware to generate sounds or to pixel processing units to swap color palettes.

Sending output to an external device is achieved through the use of the OUT instruction. This instruction typically takes two arguments, the first of which being the port to send the output stored in the C register and the second argument being a register containing a value to send.

Let’s suppose we’re working on a game and that we want to signal to our audio hardware to play some game over music when there are no more player lives left. We’ll say that our microprocessor can communicate with the audio hardware over port 0x05 and that the byte sent as output signifies which sound is to be played. For the sake of simplicity we’ll say that byte 0x00 signifies the game over audio. The assembly code for playing the audio might look something like this:

So at this point in our code the game has deemed the player must lose as life. On line 1 we decrement one from our lives variable, which points to the location in memory where the number of player lives remain in the game. We check the result of the DEC instruction on line 2 and if player lives remain (not zero) we jump to some portion of the code that lets the player play the next life. If the number of remaining player lives is zero we continue past the JP instruction.

Line 3 assumes that since the result of line 2 was not one then it must be zero and as a result we begin the process of playing the game over music by loading port number 0x05 into the C register. On line 4 we load the value 0x00 into the A register to represent the game over music and on line 5 we send the contents of the A register to the port number contained in the C register causing the music to play. On line 6 we jump to our pretend game_over function which handles displaying a game over screen and sets the machine back into attract mode.

Memory Mapped I/O

Sometimes hardware designers elect to forego the Z80 port system altogether and use address decoding circuitry to make devices available directly through the address bus. This essentially means that devices external to the Z80 would be treated exactly the same as memory and is referred to as memory mapped input/output. Reading from or writing to memory mapped devices is as simple as using the LD instruction as we’re already accustomed to doing.

A good real world example of this is Donkey Kong. In Donkey Kong the player one controls are mapped to read operations on memory address 0x7C00. If we were writing some assembly code for Donkey Kong and wanted to poll the controls for input that code would look something like this:


As the name might suggest interrupts allow external hardware to interrupt whatever the Z80 might be doing at the moment in order to do something else. Interrupts allow us to free up our microprocessor by allowing external hardware to send a signal to the microprocessor that executes code at a predetermined location. This effectively allows the microprocessor to time events as they and resume what they were doing after the request from the hardware has been fulfilled.

Interrupts come in two flavors: regular and what are known as non-maskable interrupts. The difference between the two is that regular interrupts can be ignored with the DI (disable interrupt) instruction and re-enabled with the EI (enable interrupt) instruction whereas non-maskable interrupts can’t be ignored.

Like I said earlier interrupts allow us to execute code at predetermined memory offsets which are known as interrupt vectors. The default system interrupt vectors are located at memory addresses 0x0000, 0x0010, 0x0018, 0x0020, 0x0028, 0x0030, 0x0038. The interrupt vector for non-maskable interrupts is at memory address 0x0066. Interrupt vectors can be called through the RST which takes the interrupt vector as its only argument.

Interrupt Modes

There are three different interrupt modes that are available to the Z80. These modes are known simply as mode 0, mode 1, and mode 2 interrupts. We’ll give a brief description of each mode below.

Mode 0 Interrupts

In mode 0 the interrupting device is allowed to place an instruction on the data bus for execution by the microprocessor. This instruction will tend to be a RST instruction with the interrupt vector intended to be executed.

Mode 1 Interrupts

When mode 1 interrupts are selected interrupts act similar to non-maskable interrupts. In mode 1 regular interrupts are handled only on interrupt vector 0x0038 and non-maskable interrupts are handled by interrupt vector 0x0066.

Mode 2 Interrupts

Mode 2 interrupts are the most complicated of all the interrupt modes. In mode 2 a table of 16 bit values is maintained somewhere in memory and these values act as programmable interrupt vectors. Interrupting hardware is allowed to push an 8 bit value which is concatenated with the contents of the I special register. The program is in charge of setting the contents of the I register, which acts as the high byte of the 16 bit address and the byte returned from the device acts as the low byte. This allows the system to use a larger amount of of interrupt vectors in custom locations in memory.

Z80 Assembly Part 3: Labels and Loops

In the previous article we took our first steps with writing programs for the ZX Spectrum using the Z80 assembly language. While changing background colors is a great start programs will often need to do much more in order to be useful to us. Today we’re going to look at using the various features of our assembler to write more advanced programs.


As the name implies comments allow us to leave comments in our assembly source code. Comments always begin with a semicolon and everything after that semicolon for the rest of the line is considered a comment. Here’s an example:

Generally speaking you’ll want to have your comments be a little more helpful than the example above, but it gets the job done. Good use of commenting can be extremely valuable in assisting us with reading through portions of the code and knowing what it’s doing.

Unlike instructions comments are not assembled into the executable binary when it is built.

Assembler Directives

Assemblers have built-in instructions similar to microprocessor instructions known as assembler directives. These directives typically take the form of different types of variable declarations, conditional code assembly, and directives to include assembly code from other assembly source files. In this post we’ll be focusing mainly on directives that allow us to declare variables.


The equ assembler directive allows us to declare a global value and assign a name to it:

The above line of code sets the global variable data to the value 0x1337. One thing to note about the equ directive is that variables declared with it are considered static, meaning that once the variable is assigned a value it cannot be changed.

We can use these static variables much in the same way we use other values in assembly:

We can see a familiar variable declaration on line 7 setting data to 0x1337. I’ve declared it down at the bottom since the assembler will pack variables inline where they’re declared. This means that if variables are declared directly after the org directive the first instructions that the processor would try to execute would actually be your variable data. While we could declare the variables at the top and have our first instruction jump over them we’ll just define them at the end for the sake of simplicity.

On line 3 we can see that rather than referring to our variable by its address we can refer to it by name. Loading data into the HL register pair causes HL to hold the value 0x1337 and on line 4 we use this value as a pointer to store the value 0x01 at memory address 0x1337.


The defb assembler directive allows us to declare a byte or series of bytes and assign a name to it:

As we can see this usage is practically identical to our previous usage of equ. The difference is that the above variable is a dynamic variable, meaning that the value this variable contains can be changed after it is set.

In addition to defining variables similar to equ we can define series of bytes and strings of text:

Line 1 defines a variable containing the sequential values of 1 through 5 hexadecimal. Line 2 defines a text string that we can use for printing to the monitor.

Variables declared with the defb assembler directive can be referred to in the same way as variables declared with equ. Changing variable values after initialization is as simple as loading into them like you would with memory or registers.

We can see that while we initialized power_level as 0x2328 hexadecimal we are able to change it on line 4 using a simple ld instruction.


Remember how the jumps and calls from the previous article only referred to memory addresses? You may have thought to yourself at some point that it must get tiring having to know all the memory addresses in your program to jump and call. Fortunately since we’re working with our own source code in our assembler we have the luxury of using labels rather than remembering all of our various memory address offsets.

Labels allow us to mark the memory address of the instructions they precede much in the same way we can mark the pages of a book with a bookmark:

Line 3 defines the label start which refers to the memory address prior to the load instruction on line 4, which in this case would be 0x8000 (org $8000) since it’s declared prior to any instructions or data.


Loops are a method of repeatedly executing a series of tasks until a condition or set of conditions have been met. We can implement loop functionality by defining labels and repeatedly performing either conditional or unconditional branch instructions.

For example, consider the following program:

This program will first load the value 0x01 into the A register. After loading the register the processor will then jump to the label “loop” located above the load instruction and execute the load instruction again. Since the jump at line 5 is unconditional there is no way for the processor to advance beyond line 5 of the code resulting in an infinite loop.

Now let’s consider the following example:

Unlike the previous program this program will load the value 0x01 into the A register only three times and then will return. We can see at the bottom we’ve defined a counter variable which holds the value 0x03. After loading 0x01 into the a register on lines 5 and 6 we load the address of the counter variable into HL and then decrease the value stored in that variable by one. On line 7 we check to see if the result of the dec instruction was zero and if it’s not zero (NZ) jump to the address pointed to by the loop label.

Hello Spectrum

One of the first programs programmers typically write when working with a new language or platform is the hello world program, which simply prints the text “Hello world!” to the screen. We’ll continue this tradition by writing our own version of this program which we will call hello spectrum. Once again we’ll be using the ZXSpin emulator and built-in assembler for writing and testing our programs.

In order to print to the screen we’ll first have to unlock it. Unlocking the screen is done via the function located at memory address 0x1601. This function requires that a number corresponding to the screen region to unlock be loaded into the A register prior to calling the function.

After unlocking the upper portion of the screen for writing we’ll go ahead and call a familiar function located at memory address 0x229B to set the border color of the program. This function expects a color code to be loaded into the A register prior to being called. Setting a border color isn’t really necessary to print to the screen, but it does add a little something visually to the program and allows us to easily tell when our program is running.

Now we’re ready to print to the screen via the function located at memory address 0x203C. This function expects that a memory address pointing to the start of the message be loaded into the DE register pair and that the length of the message be loaded into the BC register pair. Once called this function will loop through the message byte by byte and printing it to the screen while decrementing the value in BC for each byte printed.

Finally, we direct the processor to wait in a loop. We do this because returning after program execution will cause the screen to clear, which can make it difficult to determine whether or not the program executed as intended. We accomplish this waiting feature by repeatedly executing the nop, or no operation, instruction in a loop. As you may have guessed the nop instruction instructs the processor to basically do nothing.

Lines 3-5 of our program are the rough outline for program execution. These lines instruct the processor to execute the init_screen, print, and wait functions in that order. This area of code is what’s typically referred to as the main entry point, or main function, since line 3 is where user defined code is first executed.

Lines 8-14 flesh out the init_screen function, which is executed after the call instruction to init_screen is made on line 3. On lines 9 and 10 the upper half of the screen is unlocked by loading the value 0x02 into the A register and calling memory address 0x1601. We then turn the border color to blue on lines 12 and 13 by loading the value 0x01 into the A register and calling the address 0x229B. The ret instruction on line 14 returns us to line 4 of our program where the print function is called.

Lines 16-20 define our print function. On line 17 we load the address pointing to the beginning of the string stored in the hello variable in the DE register pair and on line 18 we load the length of our string into the BC register pair. With our BC and DE register pairs ready we call the address 0x203C on line 18, which prints our string to the Spectrum’s screen.

It’s worth noting that the byte 13 at the end of the hello variable is the terminal code for carriage return, which forces any additional printed text to be printed on the next line below the previously printed text, effectively creating a new line. When we print to the screen using the 0x203C call we’ll want to account for this printed character like we do on line 18.

Just like before with the init_screen function we use a ret instruction on line 20 to return code execution to line 5 of our program where the wait function is called.

Lines 22-25 define our program’s wait function. This function effectively does nothing other than jumping back to the top of the function after executing a nop instruction. Doing this prevents the function from returning and implements an infinite loop which allows us to view the message on the screen without it clearing.

With our program loaded into ZXSpin’s assembler let’s select File->Assemble and select OK to load the program into memory. Selecting Program->Run displays the following screen:


We can see our blue border as well as our string printed at the top of the screen. Since this program waits forever rather than returning we’ll end program execution and restore the machine state  by selecting Program->Stop and Restore from the assembler window.

Super Coin Toss

Now that we know how to print to the screen let’s do something a little more interesting with it. The next program we’ll write is a “game” called Super Coin Toss. The goal of the Super Coin Toss program will be to print a program banner and then simulate five coin tosses and print the results to the screen.

Just like with our last program the first step will be unlocking the top half of the screen with the function at address 0x1601 and setting the border color via function 0x229B. After initializing the screen we print the program’s banner like we did in our previous program with the function at address 0x203C.

After initializing the screen and printing our banner we want to grab a random number to simulate the toss of a coin. The ZX Spectrum operating system provides two variables at addresses 0x5C76 and 0x5C77 that we can add together to get a pseudo-random number. After adding the two numbers together the result is stored back into 0x5C76 in order to generate a different random number the next time this method is used.

After generating the random number we’ll examine the zeroth bit, also known as the least significant bit, and see whether or not it’s a one or a zero. Since the only possible outcomes of a coin toss are heads or tails we only need 1 bit worth of information to represent a single coin toss. We’ll choose zero to represent a toss of heads and one to represent a toss of tails and print the results accordingly.

We’ll also want to keep track of how many coins we’re tossing and limit ourselves to five tosses. After tossing a coin and printing the result we’ll want to decrement a counter value that’s initially set to 0x05. Once the counter variable reaches zero we’ll want to stop tossing coins.

We will wait at the end of our program in a similar way we did in the previous program since we want to be able to view the text output of this program as well.

Once again line 3 is our main entry point with a call to our init_screen function. Lines 17-23 are functionally identical to the init_screen function in our previous program. This function returns on line 23 returning code execution to line 4 which calls our print_banner function.

Lines 25-35 define our print_banner function. This function operates similiar to the print function in our previous program, however instead of printing one message to the screen we’ll be printing three that will serve as our program’s banner. The variables banner1, banner2, and banner3 are loaded into the DE register pair along with the string length in the BC register pair and printed with the function at 0x203C. After printing these three variables we return on line 35 to and execute the instruction on line 6, which is our call to random.

The random function is defined on lines 37-43. First we load the number stored at memory address 0x5C76 and load that into the HL register pair on line 38 and load the number stored at memory address 0x5C77 into the DE register pair on line 39. After adding the HL and DE register pairs together on line 40 the result held in the HL register pair is stored back into memory address 0x5C76 on line 41. Doing this allows a  new random number to be generated the next time we call the random function. In fact if we didn’t store the result back we would just generate the same result every time. Then on line 42 we store the contents of the L register into the A register and return on line 43.

After calling the random function we check the result that was stored in the A register on line 7. This is done by performing a logical and comparison with the and instruction using the bit pattern of the value 0x01 to determine if the least significant bit is a one or a zero. On line 8 we call print_heads if the result of the and operation was a zero. In case the result of the operation was one we run the and instruction again on line 8 to set the flags register accordingly and call print_tails on line 9.

Lines 45-55 define the print_heads and print_tails functions. These functions use the same screen printing method as our previous print and print_banner functions.

After printing the result of a coin toss the variable counter is loaded into the HL register pair on line 11 and used as a pointer for the dec instruction on line 12 to decrease the amount of available coin tosses left by one. On line 13 we jump back to loop to continue tossing coins if the result of the dec instruction was not zero. If counter does happen to reach zero we continue on to line 14 where we jump to the wait label.

Selecting Program->Run from the assembler menu should reveal a screen that looks something like the following:


Once again to return execution back to the Spectrum’s operating system select Program->Stop and Restore.

Wrapping up

By now we should be familiar with building programs capable of looping using a combination of labels and common assembler directives. Since we know what loops tend to look like now in assembly they will be easier to spot in our reverse engineering projects down the road.

In the next part of the Z80 Assembly series we’ll be looking at how the Z80 communicates with external devices.

Z80 Assembly Part 2: Loading and Branching

So we’ve had a look through both general microprocessor architecture as well as the Z80 microprocessor architecture and we’re now ready to start learning the Z80 assembly language! Exciting, isn’t it?

This post will focus on the two types of instructions that Z80 programmers will rely the most heavily on: loading instructions and branching instructions. You’ll be hard pressed to find a useful Z80 assembly program out there that doesn’t make use of either of these types of instructions, so getting a handle on them will be essential. As usual we’ll be referencing the Zilog Z80 user manual as we make our way through today’s post.

Loading Instructions

As the name implies loading instructions are instructions that tell the processor to move data from one place to another. In Z80 assembly the only instruction we usually have to worry about for moving data is the LD instruction. There are other instructions that will move data around, however these are basically souped up versions of LD, so we’ll be limiting our loading instruction exposure today to LD.

The LD instruction follows this syntax:

LD destination, source

Destination is the register or memory address that data should be loaded into. Source is the place where that data is coming from. The source can generally come from one of three places: a register, a memory location, or a number. Numbers loaded directly into memory addresses or registers are typically referred to as immediate values. If you’re curious to see all the valid combinations of registers, memory addresses, and immediate values for the LD instruction you can find them on page 68 of the Z80 user manual.

Loading Into Registers

The following line of Z80 assembly showcases loading an immediate value into a register:

LD A, $1

As we can see here we are loading a source of $1 into the destination register A. By now we’re familiar with the A register, but what’s this $1 business? In Z80 assembly the dollar sign denotes whether or not a number is being expressed in decimal or hexadecimal format. Numbers appearing with a dollar sign are hexadecimal numbers while those without are decimal. So in this case this line of assembly loads the value of 1 hexadecimal (0x01) into the A register.

Loading Into Memory

Loading values into registers is great and all, but that’s not going to get us very far unless we can store those values in memory. Let’s take a look at the following line of assembly:

LD ($8000), A

We can see that this line of code loads the value of register A into ($8000). We know that the $8000 part of the destination means 8000 hexadecimal (0x8000), but what’s the deal with the parentheses? Parentheses in the Z80 assembly language denote a memory address. In this case we are addressing the memory location 0x8000 and storing the value of the A register there.

In addition to referring to a memory address using immediate values the programmer may choose to store the memory address in a register pair and load into that. Let’s take a look at how that would be done:

LD HL, $8000
LD (HL), $1

The first line of assembly loads the value of 0x8000 into the HL register pair. In the second line we load the value 0x01 into the address of HL. Since HL currently holds the value 0x8000 this means that the value of 0x01 will be written to memory address 0x8000.

For those of you out there with programming experience in languages that use pointers such as C or C++ this is effectively an early example of pointer usage.

Branch Instructions

Z80 programs wouldn’t be very interesting if they just started at memory address 0x0000 and executed all their instructions in series. If programs simply did this not only would they predictably execute every instruction regardless of the situation but the run times of those programs would be cut pretty short as well. Branch instructions allow us to make decisions in our programs and to keep our programs from simply terminating after a short time.

The three most commonly used branch instructions in the Z80 assembly language are jump, call, and return.


As the name suggests the jump instruction, or JP, allows us to jump to any position in memory and execute the instructions located at that position. Jumps may be either conditional or unconditional. In the case of conditional jumps the flags register is used to create boolean (true or false) conditions for jumping to a memory address.

Here’s an example of an unconditional jump:

JP $1337

This line of assembly would direct the Z80 to load the value 0x1337 into the program counter where it would fetch and execute the instruction located at that same address.

Now let’s say that I’m working on the player death code for a game of mine and I want to make the game restart to the main menu after the player loses all their lives. For the sake of simplicity we’ll say that jumping to address 0x0000 will allow us to reset the game, however we only want to do this once the amount of remaining player lives reaches zero. We’ll also say that the remaining lives variable is held at memory address 0x9001. The assembly that would handle this might look something like:

DEC ($9001)
JP Z, $0

In the first line we decrement (subtract one from) the amount of player lives located at address 0x9001. If the result of decrementing the value at 0x9001 is zero then the zero flag, or Z, is set as a result. In the second line we specify the zero flag Z as our condition for jumping to memory address 0x0000. Putting these two lines together we see that if subtracting one from memory location 0x9001 results in a value of zero the Z80 is to jump to address 0x0000.

There are a few other conditions that you can specify when jumping, but we won’t be covering them all in this post. If you’re curious to see the possible conditional outcomes of the DEC instruction they can be found on page 169 of the Z80 user manual.


The CALL instruction is very similar to the jump instruction. The difference between CALL and JP however is that CALL will save its place before copying a new address to the program counter much in the same way a bookmark preserves your place in a book. The CALL instruction accomplishes this by pushing the value of the program counter onto the stack prior to modifying the program counter value . Just like jumps calls may also be conditional.

For those of you out there with previous programming experience in higher level languages the CALL instruction is the assembly equivalent of a function call. The general idea is that you load values into certain registers before issuing a CALL to your offset and these registers act as arguments to the function.

A simple example of a call would look like this:

CALL $1337

This instruction would push the current program counter onto the top of the stack and fetch the instruction located at 0x1337.


The return instruction, or RET, is the counterpart to CALL. RET is always used after you wish to return from a CALL; not doing so can and will lead to nasty bugs in programs! The usage of the return instruction is very simple:


The RET instruction pushes the top-most element of the stack into the program counter which causes the Z80 to fetch the instruction at the address stored in the program counter.

Just like with CALL and JP RET may also be used conditionally. For instance, if we only wanted to return if the zero flag is set in our flags register we would write something like:


Again, for those of you with prior programming experience in higher level languages the return instruction is basically the same thing as using a return statement. The only difference is the Z80 return instruction does not support the concept of return codes like the return statements in other languages would.

Some Assembly Required

So we’ve familiarized ourselves with the essential loading and branch instructions available to the Z80. While they may not seem like much we can actually do some pretty neat stuff just by loading values into different places and calling address offsets. Let’s try doing some Z80 assembly programming for the Sinclair ZX Spectrum line of personal computers using the ZXSpin emulator!

Before We Begin

Before we start there’s a known bug in this version of ZXSpin. You’ll want to select Tools->System, and on the option that says “When no longer active application” select the “Keep running with sound” option and save. If you don’t you’ll see this when you go to load your assembly into memory:


Getting Started

Let’s start up ZXSpin if you haven’t already and select Tools->Assembler. A window should appear looking something like:


Our first program will set the border color of the screen to blue. Since the ZX Spectrum provides operating system functions for doing this we’ll be making use of them and life will be pretty easy as a result. In order to set the border color of the screen a color value is loaded into the A register and a CALL is made to address 0x229B. Here’s the code for the program:

ORG $8000
LD A, $1
CALL $229B

The ORG $8000 line is something we haven’t come across yet. Basically the ORG statement tells the computer where in memory you want your program to be loaded. I chose 0x8000 since it’s the upper half of addressable memory and isn’t likely to collide with anything else in RAM. If we were to accidentally load our program too low in the memory addresses and overwrite areas of memory the operating system uses it could cause all sorts of problems. The RET at the end of our assembly allows control to return back to the operating system after our program has finished running.

The rest should be pretty straightforward. We load the value 0x01, which is the border color code for blue, and then we call address offset 0x229B which handles actual rendering of the border. If you’re curious to see the other color codes there are 8 in total and you can tweak the value of the A register to change the colors.

Now that we have the following code entered in our assembler we will select File->Assemble. A window will pop and you can select OK. Now select Program->Run. You should now see that the border has changed to a glorious blue like so:


Wrapping up

Congratulations on writing your first ZX Spectrum program! In the next post we’ll be going deeper down the rabbit hole. We’ll be exploring the concepts of looping as well as how looping is implemented when programming in assembly. We’ll also be working further with the ZX Spectrum and ZXSpin to test out more programs that do some more interesting things!

Z80 Assembly Part 1: Z80 Microprocessor Architecture

In my previous post we covered general microprocessor architecture concepts. In this post we’ll be covering the Z80 architecture from a programming perspective and will follow along closely with the Zilog Z80 user manual. Feel free to use the Z80 user manual to follow along and for future use as a reference source.

Z80 Block Diagram


Remember the block diagram from the general microprocessor post? Well this is the Z80 block diagram. We can see there’s a lot of similarities here and that not much is really different between the two diagrams. We have our control unit, our ALU, our registers, and our buses.

Z80 Address Bus

We can see that the address bus width on the Z80 is 16 bits, which means up to 65,536 bytes (64kb) can be directly addressed by the processsor’s address bus. Since addresses start from zero this means the address space immediately usable to the Z80 ranges from 0x0000 (0000 decimal) to 0xFFFF (65,535 decimal). There is a technique known as bank switching which allows processors to address more memory than their address bus would normally allow, but we’ll keep our focus to the immediately accessable 64kb of memory for now.

It’s also worth noting that when the Z80 boots up or is reset it will begin fetching instructions from address 0x0000. For this reason you will generally find the program ROM at the lower memory addresses and program RAM at higher memory addresses.

Z80 Registers


When programming for the Z80 we’ll be spending a majority of our time working with registers. The Z80 provides us with a main and alternate register set each consisting of six general purpose registers, an accumulator register, and a flags register. The alternate registers are used mainly as extra storage space so for now we’ll be focusing on the main register set.

In addition to the main and alternate register sets are the special purpose registers. Counted among these are the index registers, stack pointer, and program counter.

General Purpose Registers

The Z80 provides six general purpose registers: B, C, D, E, H, and L. Each of these general purpose registers is 8 bits in width which means that each register can hold a value between 0x00 and 0xFF (255 decimal).

General purpose registers may also be concatenated, or paired together, to form 16 bit values. This allows for things like 16 bit math and accessing the entire address space of the 16 bit address bus.

The valid 16 bit general purpose register pairs are BC, DE, and HL. For example if register B is set to 0x12 and register C is set to 0x34 then the register pair BC is set to 0x1234.

The accumulator and flags register pair AF is also considered a valid register pair, but is generally more useful when debugging Z80 programs than it is when actually programming them.

Index Registers

The Z80 provides two 16 bit registers, IX and IY, that are known as index registers. These registers are used to point to base addresses and use values in other registers as offsets to that address.

This is useful for times when you have data packed together, know the width of that data, and want to operate on one or more pieces of that data. This concept is handled in higher level programming languages by arrays, where many pieces of data of the same type are organized one after another and referred to by an offset.

For example, let’s say I have a high score entries table in my game that is located at memory address 0x1000 and that each high score record is 5 bytes worth of information. If IX equals 0x1000 the first entry would be expressed as IX + 0x00, the second as IX + 0x05, and so on. We would store these offsets in one of the general purpose registers and increment it by 0x05 for each additional record we want.

Accumulator Register

The accumulator register A will automatically contain, or accumulate values as the result of certain instructions. It’s worth noting that this register can be used much like any of the other general purpose registers, but that performing certain mathematical or logical operations can change the values you’ve stored in the A register.

Flags Register

The flags register stores a bit pattern for 8 different possible boolean conditions that may have changed as the result of an executed instruction. We’ll examine each flag in depth as we start working with specific Z80 assembly instructions.

Program Counter Register

The program counter register or PC register is a 16 bit register which holds the current address of the instruction fetched from memory. After an instruction is executed this register is incremented to fetch the next instruction. On a reset of the Z80 the PC register is set to 0x0000 where it fetches its first instruction and increments from there.

Stack Pointer Register

The stack pointer points to the current memory address of the top of the stack. The stack is used as a temporary storage space in memory.

Conceptually the stack is like a stack of any items, which is to say that anything that was first to be stacked will be the last item we get to when we pull everything off the stack one by one. Conversely the first item you can pull off of the stack is the last item that was put on the top. This is what’s known as a Last In First Out,or LIFO, stack. We’ll cover the stack more in depth when we examine stack manipulation instructions such as push and pop.

Z80 Interrupts and Interrupt Handlers

Interrupts allow external devices to talk to a microprocessor. These devices might be button or joystick switches, key switch presses from a keyboard, or any number of other pieces of hardware out there. These interrupts cause the Z80 to stop what it’s doing and execute code based on the interrupt occurring and will resume whatever it was doing after handling the device.

Interrupts come in two flavors: maskable and non-maskable. The difference between maskable and non-maskable interrupts is that non-maskable interrupts cannot be ignored by software where as maskable interrupts can. Non-maskable interrupts are handled by code at the offset 0x66 while several other interrupt response modes govern interrupt response for maskable interrupts.

Mode 0 Interrupts

Mode 0 interrupts allow for a device to specify instructions for the processor to execute and require the device to return control to the processor through a restart instruction.

Mode 1 Interrupts

Mode 1 interrupts work just like non-maskable interrupts except the code for handling the device is executed at offset 0x38 instead of 0x66.

Mode 2 Interrupts

Mode 2 interrupts allow a program to maintain a table of 16 bit addresses that point to memory offsets of where interrupt handling code should be executed. The table of addresses has no fixed place in memory and the address is determined by loading the special register I with a value and pairing it with an 8 bit value returned by the interrupting device.


The Z80 is what’s known as a little endian processor. This means that when multiple bytes of data are stored in memory the bytes are flipped so that the least significant byte is first and the most significant byte is last. For instance the little endian representation of the 16 bit value 0x1234 would be 0x3412 and the little endian representation of the 32 bit value 0x12345678 would be 0x78563412.

Knowing that your data is being stored in reverse byte-wise isn’t so helpful for when you’re writing programs for the Z80, but it’s invaluable for debugging and reverse engineering Z80 programs!

Wrapping Up

That was the high level overview of the Z80 architecture where Z80 programming is concerned. In the next article we’ll get started with writing some Z80 assembly!

Microprocessor Fundamentals

In this post we’ll be looking at microprocessor architectures in general as well as how they work. Microprocessors are at the heart of any computer system, arcade games included, and in order to change the behavior of various games we’ll need to know more about how these devices interpret data and act upon it.

What is a microprocessor?

Simply put, microprocessors are the brains of any computer system. They are programmable general purpose devices, which essentially means they can interface with any sort of hardware and are capable of performing any number of tasks with the hardware by reading data from a memory device.

We refer to the data stored on the memory device as instructions or opcodes (short for operation code), which as the name suggests instruct the processor on which operation it is to perform. These instructions are typically held on some kind of permanent storage, such as a ROM (read only memory) chip or hard drive; in the case of older arcade systems these instructions would be held in the ROM chips on the game’s circuit boards.

It’s helpful to know that while there are many different microprocessor architectures out there many architectures follow the same design concepts. These different architectures will generally always consist of the following components: a control unit, an arithmetic and logic unit, registers, and a system bus. Let’s explain these components in further detail below.

Block diagram illustrating common processor components.

Control Unit

As the name implies the control unit, or CU, is in charge of controlling the processor’s actions. It is responsible for fetching instructions from memory, decoding fetched instructions, and signaling the various other components of the processor to get work done. You can think of the control unit as the foreman of a construction company; the foreman takes a look at the construction plan while supervising and instructing various construction teams as to what they should be doing in order to accomplish a common task.

The control unit is driven by an oscillating clock, a device which outputs alternating high/low voltages at a constant frequency, which allows the control unit to synchronize the various components of the processor and governs the speed at which the processor operates. These days clock speeds are typically expressed in megahertz or gigahertz and this is what people are referring to when talking about processor or core clock speeds.

Arithmetic and Logic Unit

The arithmetic and logic unit, or ALU, is responsible for handling mathematical and logical operations. If something needs to be added, subtracted, multiplied, or divided chances are this is going to be handled by the ALU. In addition to your run-of-the-mill mathematical operations the ALU also handles logical operations such as boolean comparisons and bit shift operations, which we’ll cover in later material.


Consider for a moment the relation between software and memory on your computer. In order for the software on your computer to do anything really useful it will need to periodically store and recall data from RAM for all sorts of things like performing mathematical calculations, implementing timing mechanisms, or saving user input for later use.

Registers are like small pieces of RAM in your microprocessor that are intended for holding data for relatively short periods of time. In programming terms you can think of registers as the places where instruction arguments are held, which is to say that microprocessor instructions will expect data to be in certain registers at the time the instruction is executed and will perform operations using the data in these registers. Generally speaking there are four common classes of registers that can be seen in any processor architecture: program counter registers, flag registers, accumulator registers, and general purpose registers. Let’s examine each class of register individually below.

Program Counter Register

The program counter register is a register which all microprocessors share in common. They might not always be referred to specifically as the program counter, but they will always serve the same function. The purpose of the program counter is to store the address in memory for the next instruction to be executed and is necessary for proper execution of a program. After the control unit fetches, decodes, and executes an instruction the program counter is incremented which allows the control unit to execute the next instruction at that address.

Flag Register

The name for the flag register may differ from architecture to architecture, but much like the program counter the usage between architectures will remain constant. Flag registers are registers that hold boolean information about the previously executed instruction. Each bit of the flag register corresponds to a true/false condition.

For example, let’s say that I’m programming a processor to continuously decrement a value in memory by 1, but I want to stop decrementing once that value is zero. The most efficient way to determine whether or not that value is equal to zero is to examine the zero, or Z, bit of the flags register. If the zero bit ends up being a one then this indicates that the result of the previous operation was zero, otherwise the resulting value was non-zero.

If this is unclear don’t worry: we’ll go into flags in and their usage in greater detail once we start examining specific architectures, but for now it’s just important to be aware that they exist and that they’re used to get simple true/false answers to questions about the results of the previous instruction.

Accumulator Registers

Like the name implies accumulator registers are registers that accumulate values as the result of an executed instruction. In more plain terms this means that registered designated as accumulator registers will automatically hold the results of an operation. Most if not all processor architectures contain at least one accumulator register.

Probably the most pervasive example of accumulator registers accumulating values is their use in math operations. For example, if I were to add the numbers 1 and 2 together the accumulator register would hold the value 3 as a result of the addition operation.

General Purpose Registers

General purpose registers are registers that are meant to hold data for whatever purposes the programmer sees fit. They will not accumulate values as the result of instruction operations and will store data loaded into them until new data is loaded in its place.

Register Width

Incidentally the size of a processor’s registers, known as register width, is what distinguishes 8/16/32/64 bit processors from one another. Some misguided individuals will tell you that an 8 bit game system is an 8 bit system because the color palette allows 8 bits of color depth, but this is not the case. An 8 bit game system is an 8 bit system simply because each general purpose register on the processor is able to hold 8 bits worth of information.

Register width, specifically the register width of the program counter, also determines the maximum amount of memory a processor can feasibly use. For instance a 16 bit program counter can feasibly access up to 65,536 bytes worth of memory.

System Bus

Much like how a city transit bus is responsible for transporting people to and from various destinations a system bus is responsible is transferring information between various processor components and devices external to the processor. The three classes of system bus common to microprocessors are known as the control bus, the address bus, and the data bus.

Control Bus

The control bus is used by the control unit to signal various components of the processor to perform operations and is typically used for transferring registry values between the ALU or data bus.

Address Bus

When one accesses memory from ROM, RAM, or an external device one is said to be addressing memory. Memory addresses are expressed as hexadecimal numbers starting from zero and increment by one for each following address. These addresses represent one byte (8 bits) of the total memory available to the processor.

The address bus allows the processor to signal a memory address to memory which allows the memory to return data to the processor over the data bus.

Data Bus

The data bus allows for the storage and retrieval of data between memory, external devices, and the processor’s registers.

Wrapping Up

So we’ve covered many of the high level concepts concerning microprocessor fundamentals and gained some familiarity with the basic terminology associated with these concepts. If it’s not all making complete sense that’s OK, we’ll gain more familiarity with these concepts by examining the Z80 microprocessor architecture and Z80 programming in assembly language in later posts.

Intro to Hexadecimal

One of the first topics we’ll cover here in our quest to reverse engineer arcade games is the hexadecimal numbering system. Those of you familiar with the hexadecimal numbering system can safely skip this article.

Programming microprocessors, and by extension reverse engineering the software running on them, typically involves the use of numbering systems other than the standard decimal system. As humans we are used to dealing with decimal, or base 10, however computers typically deal in number systems known as hexadecimal or binary.

As the name might suggest hexadecimal is a base 16 number system. Now at this point you may be asking yourself, “Self, if the numbers we use everyday are 10 digits per place then how on Earth do we work with a numbers that are 16 digits per place?”. To answer that let’s take a quick look at a comparison between decimal and hexadecimal systems:

hex_dec_comparison (2)

Basically the answer is that we cheat by substituting letters for numbers. We can see that between decimal and hexadecimal it’s business as usual from 0-9, after which A becomes the symbol for 10, B becomes the symbol for 11, and so on. By assigning numerical values to symbols, in our case letters of the alphabet, we’re able to store more and wider ranges of information in fewer digits than would be possible under the decimal system.

So we know what hexadecimal is, but how do we make use of it? Probably the most common thing you’ll run into is the need to convert from hexadecimal to decimal.

Let’s consider how the number 9001 in decimal can be expressed:
(9 * 10^3) + (0 * 10^2) + (0 * 10^1) + 1

Similarly let’s examine the number 9001 hexadecimal:
(9 * 16^3) + (0 * 16^2) + (0 * 16^1) + 1

We can think of these numbers as the sum of each digit multiplied by the base to the exponent of the number of places to the left the digit is. Knowing this, let’s convert the hexadecimal number 9AB to decimal:
9AB = (9 * 16^2) + (10 * 16^1) + 11
(9 * 16^2) + (10 * 16^1) + 11 = (2304) + (160) + 11
(2304) + (160) + 11 = 2475
9AB hexadecimal = 2475 decimal

For those of you who may find this a bit intimidating don’t worry; you won’t really ever be expected to do manual calculations and conversions by hand unless you want to. Right now it’s just important to know what hexadecimal is and how it compares to the decimal system.

Now that we’ve got all that out of the way I have a confession to make: practically every time I need to perform a decimal/hexadecimal conversion I cheat. For most practical applications simply using a calculator in “programmer mode” is more than sufficient. Let’s take quick look at how this is done.

For this exercise we’ll convert the decimal number 1234 to its hexadecimal equivalent using the calculator provided with Windows. Go ahead and open up the calculator and you should have something that looks like this:

Screen Shot 2015-02-10 at 12.25.46 PM

Next, let’s set our calculator to “programmer mode” by selecting View and then selecting Programmer. You should now see something like this:

Screen Shot 2015-02-10 at 12.49.01 PM

We can see that our calculator is currently in decimal mode as indicated by the selected “Dec” radio button on the left. Since we’re in decimal mode we’ll want to enter our decimal number that we want to convert, so let’s enter “1234” now.

Screen Shot 2015-02-10 at 12.54.51 PM

Now all that’s left to do is select the “Hex” radio button option to put the calculator into hexadecimal mode. The calculator will already convert the value displayed earlier into its hexadecimal equivalent when you do this:

Screen Shot 2015-02-10 at 12.56.51 PM

As we can see 1234 has been converted to its hexadecimal equivalent of 4D2. Performing hexadecimal to decimal conversions works the same way except in reverse. This means that you would start in hexadecimal mode, enter a value, and then select decimal mode to retrieve the equivalent decimal value.

That’s about all I can think of on the topic of hexadecimal for now. It may seem weird to you at first when you begin working with it, but after some time working with it I think you will find that it will feel like second nature.