Assembly Language Fundamentals

Intermediate
Version:
1.0

Pointers and Address Calculation

So far, we’ve worked with strings and arrays by directly accessing memory with simple offsets. This approach works well when data is stored sequentially. But as programs grow, we often need something more powerful — a way to store the location of data and then use that location to access or manipulate it.

This is where pointers come in. A pointer is simply a memory address stored in memory. Once you understand how to calculate and use them, you’ll unlock the ability to build dynamic data structures, lookup tables of addresses, and much more.

This lesson is entirely dedicated to understanding how pointers work — step by step.

What You’ll Learn

What Is a Pointer?

A pointer is simply the memory address of another piece of data.

For example:

data:
   DEFB 42

ptr:
   DEFW data

Here:

This means ptr itself contains two bytes — the low byte and high byte of the address.

Storing and Loading Pointers

Let’s say we want to load the value stored at data using the pointer:

   LD HL, ptr        ; HL points to the pointer
   LD E, (HL)        ; Load low byte into E
   INC HL
   LD D, (HL)        ; Load high byte into D
   EX DE, HL         ; HL now holds the address stored in ptr
   LD A, (HL)        ; A now contains 42

Step-by-step explanation:

  1. LD HL, ptr – HL points to the location of the pointer.
  2. LD E, (HL) / INC HL / LD D, (HL) – Load the two bytes of the pointer into DE.
  3. EX DE, HL – Swap DE and HL, so now HL contains the address the pointer was storing.
  4. LD A, (HL) – Finally, read the data from that address.

This is known as dereferencing a pointer — following it to the data it points to.

Why Two Bytes?

Because Z80 uses 16-bit memory addresses, a pointer always requires two bytes — one for the low 8 bits and one for the high 8 bits.

For example, if data is at address 0xC000, the pointer will store:

When you read the pointer, you must read both bytes and combine them to reconstruct the full address.

Pointer Tables

Pointers become most powerful when you store many of them in a table — an array of addresses.

For example:

message1: DEFB "Ready", 0
message2: DEFB "Set", 0
message3: DEFB "Go!", 0

table:
   DEFW message1
   DEFW message2
   DEFW message3

Now table is an array of three pointers. Each entry is two bytes long.

Calculating Pointer Offsets

Each pointer is two bytes, so the address of the Nth pointer is:

table + (N * 2)

For example, to load the second pointer (message2):

   LD A, 1           ; Index (0-based)
   ADD A, A          ; Multiply by 2 (2 bytes per pointer)
   LD L, A
   LD H, 0
   LD DE, table
   ADD HL, DE        ; HL = address of second pointer

   LD E, (HL)        ; Load low byte
   INC HL
   LD D, (HL)        ; Load high byte
   EX DE, HL         ; HL = pointer to message2
   LD A, (HL)        ; Load first byte of message2

Let’s break this down carefully:

  1. Multiply index by 2 – Because each pointer is 2 bytes.
  2. Add base address – Moves HL to the correct pointer’s location.
  3. Load the pointer – Retrieve low and high bytes separately.
  4. Follow the pointer – Swap into HL and use it.

This four-step pattern is the backbone of how games and operating systems store and retrieve structured data.

Pointers vs Arrays

ArraysPointersStore data directlyStore addresses of dataAccess by base + offsetAccess by base + (index × 2), then followFixed structureFlexible, data can be anywhereNo indirectionRequires dereferencing

Arrays are simple and fast, but pointers are far more flexible — they allow you to rearrange data, store variable-length strings, and build complex structures without hardcoding addresses.

Exercises

  1. Basic Pointer Dereference:
    Create a pointer to a single byte in memory. Load the byte’s value into A by following the pointer.
  2. Pointer Table Access:
    Create a table of three pointers to three different strings. Write code that retrieves the second one and prints it.
  3. Dynamic Access:
    Store an index in memory. Use it to choose a pointer from a table and print the string it points to.
  4. Challenge – Two-Level Indirection:
    Try creating a pointer to a pointer (a table of table addresses) and follow it to the final data. (This will stretch your understanding of indirection.)

Summary

Previous Module
Next Module