11 Oct

Ilkke asked me how to create a nice chiptune snare. This question escalated into a challenge to write a little tutorial about how to make less boring chiptune drums. I used Klystrack to experiment but the basic theory holds for most programs and sound chips. You can hear the results below.

### Hi-hat

We’ll start with the easiest drum. Generally, just a stable noise waveform with a sharpish attack and a longer decay is enough. If a filter is available, filtering out the low frequencies with a high or band-pass filter helps to make the other rhythm sounds stand out – you don’t want the hi-hat to drown out the snare. For a shaker type sound, make the attack phase longer.

### Kick drum

Kick drum is a quite simple drum as well, if you need a basic techno kick or a longer 808-style sine wave oomph. The theory behind a basic kick goes like this: start high, finish low. Make the amplitude and the frequency drop sharply. If you hear a sharp decay in the frequency, you will perceive it like something punching through the other sounds. See an example below, it’s a generic 909 bass drum. However, if we look at a more interesting (and louder) kick sound, we will notice it’s not just a sine wave. There are higher frequencies there even though they are not that pronounced. A good thing to do is to combine the soft sine wave (triangle wave is a nice approximation) with something sharper and louder in the first moments of the kick. Try having a few milliseconds worth of square wave and/or noise in the beginning of the drum. Then continue as usual with a low-frequency tail.

To simulate a distorted drum, use the square waveform. As in the example below, you can see a hard-limited sine wave looks like square wave: An analysis of the kick drum sound used in Auf Wiedersehen Monty confirms the ideas discussed above: […]

1. it begins with a triangle wave, which is active for 20 mS [one program tick]

2. thereafter there is a short noise part for 20 mS

3. it ends with a pulse wave, the frequency and amplitude gets lower

### Snare drum

Creating a convincing snare drum starts to get a bit complicated. It helps if we once again look at what is actually happening in a good snare sound. As you can see, the snare sound consists of noise and a lower sine wave sound, the low frequency hum is the drum membrane vibrating. Also, the beginning of the hit the low sine wave is louder than the noisy rattle which adds a nice punch. We can emulate this by having a short pure triangle wave tone (use square wave to make it louder) in the beginning and change it to noise a moment later. As you can see, the waveform now looks a bit more like the original and has a beefy attack.

Another method to make an interesting snare often used by e.g. Rob Hubbard (I think) is to vary the noise frequency with a multi-octave arpeggio. If you need a real world analogue, you could think the cycling noise frequency (low-hi-low-hi-etc.) as reverberation.

### Closing thoughts and examples

So, what did we learn? Nature is interesting because it has tons of variation and is never static or perfect. For chiptune drums, this means: never use a single waveform or frequency.

1. Kick: Triangle wave only

2. Kick: Square wave

3. Kick: Square wave attack, triangle wave delay

4. Snare: Noise mixed with square and triangle, pure noise decay

5. Snare: Arpeggiated noise

02 Jan

## Introduction

Here’s an audio compression scheme I came up for my tracker project. Generally, I wanted an algorithm that meets these three conditions:

• Has to compress mono audio losslessly
• Has to be simple, should be just one source code file or 500 lines of C code at most
• Should compress 8-bit audio that’s upscaled to 16-bits to near 8 bits per sample

The lossless requirement is obvious and the simplicity gives the benefit of not having to rely on external libraries (e.g. when using something like RAR compression) and not being overkill. The audio data is not huge in any case so the compression ratio doesn’t have to be exceptional. The last requirement is the most important: in klystrack, it is possible to load raw 8-bit audio files, usually originally from the Amiga computer. The native audio format in klystrack is 16-bit, however, so when saving the audio data there always are 8 redundant bits and the file will generally be twice as large as the original file. Also, since the software deals with low-fidelity aesthetics (chiptunes), the audio data might be even lower quality than 8-bit.

While there’s the option of detecting the bit fidelity and only saving the most significant bits, I thought it should be quite simple to devise an algorithm that automatically detects and leaves the redundant bits out while still doing some compression for even 16-bit audio data.

## The algorithm

In short, the algorithm does this:

1. Delta code input data

2. Gray code the data from above

3. Reorder the data from above in bit planes. I.e. first output the least significant bit of all samples, then the next bit for all samples and so forth

4. Run-length encode the above data exploiting the fact you only need to store the distance between bit flips

### Delta encoding

By delta encoding the audio data, we can exploit the fact audio data very often changes just a little between samples and so the generated delta values (the amount of change) need fewer bits to be expressed. However, you can’t store 16-bit data with e.g. 8-bit delta values since it is not assured the change is always small enough to be stored in 8 bits, requiring the use of 16-bit delta values in the worst case.

### Gray encoding

Using gray codes we can minimize the amount of bit flipping that happens (e.g. when a value changes between 7 and 8, where the bits change from 0111 to 1000 — three bits flip even if the value changes by one) to one bit flip between any two neighboring values. In practice, this doesn’t always result in better compression and sometimes it even can hurt the compression ratio.

### Reordering data in bit planes

This step is the key to good compression in our case. Between two neighboring delta values, it’s common that most of the bits are the same and it’s common for one bit to stay completely static for the whole data. So, we take the bits from all samples and sort them so that all significant bits come before the lesser bits. The result is long runs of bits, usually in the most significant bits.

### Run-length encoding

Finally, we take the encoded and reordered data and store the bits using RLE. While RLE usually stores the repeated data and the number of repetitions, we can exploit the fact the data is always zero or one, always changing between runs — only the starting state of the bit needs to be stored. Elias gamma coding is used to store the run-lengths since in most cases the run length is in the range of a few bits (for completely empty bit planes we can use special flags that tell they’re all zero or one) and tend to fall in 1-4 bits. With gamma coding it’s possible to store the number 1 by using one bit, the number two takes three bits.

Also, for some bit planes a raw dump of bits with no compression should be used if compression would result in much longer data than the original. There are also two special codes for the bit planes, in cases where all bits are zero or one (common if the audio data is quiet, i.e. uses only a subrange of the 16-bit resolution) or if the data is offset by a constant.

Depending on implementation the total overhead is around two bits per bit plane (i.e. 32 bits or two bytes for 16-bit audio data), not including the header data describing the compressed data (data length, uncompressed length). Since there’s the possibility of a raw dump of bits, in the worst case (none of the bit planes compress at all), the data size grows only by two bytes. A completely quiet (all zero bits) data will take only two bytes for any length.

## Improvements

My implementation only compresses the whole audio data as one big chunk but this can be easily improved to compress in chunks. This should improve the compression when there are parts where the audio is quiet and most of the bits will be zero.

Stereo audio can be compressed by compressing two chunks of data, one for both channels. The other channel should be delta coded compared to the other channel since in stereo audio the two channels often heavily correlate.

Since the Gray coding may even hurt compression, there should be the option to disable the coding for chunks. This can be simply by brute force, comparing which combination of options produces the shortest compressed data.

## Compression results

The following contains example compression results. The Option column tells different configurations where the input data is delta coded and/or Gray coded. This shows how different preprocessing steps work better for different kinds of input data.

Legend: 0 = no coding before compression, 1 = delta coding, 2 = Gray coding, 3 = delta and Gray coding.

Options Ratio
Sine wave (loud)
0 73.1 %
1 29.6 %
2 67.9 %
3 22.6 %
White noise (loud)
0 100.0 %
1 100.0 %
2 99.8 %
3 100.0 %
Complex wave
0 75.3 %
1 38.2 %
2 69.6 %
3 30.0 %
Options Ratio
Sine wave and noise
0 84.7 %
1 100.0 %
2 80.9 %
3 80.9 %
White noise (quiet)
0 50.0 %
1 100.0 %
2 50.0 %
3 55.9 %
Complex wave (saturated)
0 38.9 %
1 21.9 %
2 37.2 %
3 17.7 %

## Source code

```#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char Uint8;
typedef short int Sint16;
typedef unsigned short int Uint16;
typedef int Sint32;
typedef unsigned int Uint32;

/* bitpack() bit plane codes */
enum
{
BITPACK_STATIC0,
BITPACK_STATIC1,
BITPACK_LITERAL,
BITPACK_RLE
};

/* bitstream writer helpers*/
typedef struct
{
Uint8 *buffer;
Uint32 byte, size_byte; 	// Read/write position counters (byte component)
Uint8 bit, size_bit;		// Read/write position counters (bit component)
} BitPtr;

#define BIT_BLOCK_SIZE 1024 // Bitstream buffer allocation increment size

void bit_init(BitPtr *p, Uint8 *buffer, Uint32 size_bytes, Uint8 size_bits)
{
memset(p, 0, sizeof(*p));
p->buffer = buffer;
p->size_byte = size_bytes;
p->size_bit = size_bits;
}

void bit_deinit(BitPtr *p)
{
free(p->buffer);
memset(p, 0, sizeof(*p));
}

void bit_rewind(BitPtr *p)
{
p->bit = p->byte = 0;
}

void bit_seek(BitPtr *p, Uint32 byte, Uint8 bit)
{
p->byte = byte;
p->bit = bit;
}

void bit_w(BitPtr *p, int bit)
{
if (p->bit == 0 && !(p->byte & (BIT_BLOCK_SIZE - 1)))
{
// If write position modulo BIT_BLOCK_SIZE is zero, allocate a larger buffer
p->buffer = realloc(p->buffer, p->byte + BIT_BLOCK_SIZE);
}

if (bit)
p->buffer[p->byte] |= 1 << p->bit;
else
p->buffer[p->byte] &= ~(1 << p->bit);

++p->bit;

if (p->bit == 8)
{
p->bit = 0;
++p->byte;
}

p->size_byte = p->byte;
p->size_bit = p->bit;
}

int bit_r(BitPtr *p)
{
if (p->size_byte <= p->byte && p->size_bit <= p->bit)
return -1;

int bit = (p->buffer[p->byte] & (1 << p->bit)) != 0;

++p->bit;

if (p->bit == 8)
{
p->bit = 0;
++p->byte;
}

return bit;
}

/* Write Elias gamma coded value (has to be nonzero) */
void bit_wgamma(BitPtr *p, Uint32 value)
{
int l = log2(value);

for (int a=0; a < l; a++)
bit_w(p, 0); //put 0s to indicate how many bits will follow

bit_w(p, 1);      //mark the end of the 0s

for (int a=l-1; a >= 0; a--) //Write the bits as plain binary
{
bit_w(p, value & 1 << a);
}

}

/* Read Elias gamma coded value, zero return value signals read error */
Uint32 bit_rgamma(BitPtr *p)
{
int numberBits = 0;
int bit;
while (!(bit = bit_r(p)))
numberBits++; //keep on reading until we fetch a one...

if (bit < 0) return 0;

Uint32 current = 0;
for (int a=numberBits-1; a >= 0; a--) //Read numberBits bits
{
if ((bit = bit_r(p)))
current |= 1 << a;

if (bit < 0) return 0;
}
current |= 1 << numberBits; //last bit isn't encoded!

return current;
}

Sint32 bit_rbits(BitPtr *p, Uint8 bits)
{
Sint32 rval = 0;

for (int i = 0 ; i < bits ; ++i)
{
int bit = bit_r(p);

if (bit < 0) return -1;

rval |= bit << i;
}

return rval;
}

/* Write bits bits of v */
void bit_wbits(BitPtr *p, Uint32 v, Uint8 bits)
{
for (int i = 0 ; i < bits ; ++i)
{
bit_w(p, v & (1 << i));
}
}

/* Gray and delta encoding helpers */
static inline Uint16 gray(Uint16 v)
{
return v ^ (v >> 1);
}

static inline Uint16 degray(Uint16 v)
{
v ^= (v>>8);
v ^= (v>>4);
v ^= (v>>2);
v ^= (v>>1);
return v;
}

static void delta_encode(Sint16 *buffer, const int n)
{
Sint32 delta = 0;
Sint32 original;
for (int i = 0; i < n; ++i)
{
original = buffer[i];
Sint32 x = original - delta;
buffer[i] = gray(x + 32768); // Gray code can not be negative
delta = original;
}
}

static void delta_decode(Sint16 *buffer, const int n)
{
Sint32 delta = 0;
for (int i = 0; i < n; ++i)
{
Sint32 v = degray(buffer[i]);
buffer[i] = (v - 32768) + delta;
delta = buffer[i];
}
}

/* Compress 16-bit signed data into bitstream, return compressed data size in packed_size (in bits) */
Uint8 * bitpack(const Sint16 *_buffer, const int n, Uint32 *packed_size)
{
BitPtr bp;
bit_init(&bp, NULL, 0, 0);

Sint16 *buffer = malloc(sizeof(Sint16) * n);
memcpy(buffer, _buffer, sizeof(Sint16) * n);

delta_encode(buffer, n);

for (int plane = 0 ; plane < sizeof(*buffer) * 8 ; ++plane)
{
const Sint16 mask = 1 << plane;
int bit = mask & *buffer;

int type = BITPACK_STATIC0 | (bit != 0);

for (int i = 0 ; i < n ; ++i)
if ((buffer[i] & mask) != bit)
{
// Was not all zeros or all ones, needs to compress
type = BITPACK_RLE;
break;
}

Uint32 p_byte = bp.byte;
Uint32 p_bit = bp.bit;

again:

bit_wbits(&bp, type, 2);

switch (type)
{
case BITPACK_LITERAL:
for (int i = 0 ; i < n ; ++i)
break;

case BITPACK_STATIC0:
case BITPACK_STATIC1:
// No data needed, bit plane is all zeros or all ones
break;

case BITPACK_RLE:
{
// Write starting bit state
bit_w(&bp, bit);

Uint32 ctr = 0;

for (int i = 0 ; i < n ; ++i)
{
if (((buffer[i] & mask) == bit))
++ctr;

if ((buffer[i] & mask) != bit)
{
bit_wgamma(&bp, ctr);

ctr = 1;

// Flip the bit (no neighboring bits are the same state)
}
}

if (ctr != 0) bit_wgamma(&bp, ctr);

if ((bp.byte * 8 + bp.bit) - (p_byte * 8 + p_bit) > n + 2)
{
// RLE gave longer data than the original, dump data instead
bit_seek(&bp, p_byte, p_bit);
type = BITPACK_LITERAL;
goto again;
}

}
break;
}

}

free(buffer);

*packed_size = bp.byte * 8 + bp.bit;

return bp.buffer;
}

/* Decompress compressed bitstream into 16-bit signed data, decompress at most packed_size bits
unpacked_size tells the function the data length (important)
*/
Sint16 * bitunpack(const Uint8 *packed_data, const Uint32 packed_size, Uint32 unpacked_size)
{
BitPtr bp;
bit_init(&bp, (Uint8*)packed_data, packed_size / 8, packed_size & 7);

Sint16 *buffer = calloc(unpacked_size, sizeof(Sint16));

for (int plane = 0 ; plane < sizeof(*buffer) * 8 ; ++plane)
{
const Sint16 mask = 1 << plane;

Sint32 type = bit_rbits(&bp, 2);

if (type < 0) goto read_error;

switch (type)
{
case BITPACK_LITERAL:
for (int i = 0 ; i < unpacked_size ; ++i)
{
int bit = bit_r(&bp);
if (bit < 0) goto read_error;
}
break;

case BITPACK_STATIC0:
// Data bits are zero by default, no action needed
break;

case BITPACK_STATIC1:
// Fill bit plane with set/unset bit
for (int i = 0 ; i < unpacked_size ; ++i)
break;

case BITPACK_RLE:
{
// Read the starting bit status
int bit = bit_r(&bp);

if (bit < 0) goto read_error;

if (bit) bit = mask; else bit = 0;

buffer |= bit;

for (int i = 0 ; i < unpacked_size ; )
{
Uint32 ctr = bit_rgamma(&bp);

if (ctr == 0) goto read_error;

for (; i < unpacked_size && ctr ; ++i, --ctr)
buffer[i] |= bit;

// Flip the bit (neighboring bits are always different)
}
}
break;
}
}

delta_decode(buffer, unpacked_size);

if (0)
{
free(buffer);
return NULL;
}

return buffer;
}

/* Generate test data, 0 is a sine wave and 1 generates noise */
void getdata(Sint16 *buffer, const int n, int t)
{
switch (t)
{
case 0:
for (int i = 0 ; i < n ; ++i)
{
buffer[i] = (((int)(sin((float)i / 1000 * 3.141 * 2) * 20000)));
}

break;

case 1:
for (int i = 0 ; i < n ; ++i)
buffer[i] = (rand() * 4);

break;
}
}

int main(int argc, char **argv)
{
const int n = 100000;

Sint16 *b = malloc(sizeof(Sint16) * n);
Uint32 packed_size;

getdata(b, n, 0);

Uint8 * data = bitpack(b, n, &packed_size);
Sint16 * udata = bitunpack(data, packed_size, n);

if (udata == NULL || memcmp(b, udata, sizeof(Sint16) * n) != 0)
{
printf("Upack failed\n");
}
else
{
printf("Compression ratio %.1f %%\n", (float)100 * packed_size / (n * sizeof(Sint16) * 8));

}

free(data);
free(udata);
free(b);

return 0;
}
```
09 Nov

This is the second part of the epic (two three-part) series of articles about tiny intros, the previous part was an essay about 256-byte intros in general.

Ed. note: Since this article seems to take forever to finish, here’s the first half of it. The (hopefully) final part will detail more of the specifics. As stated earlier, tiny intros written in assembly language fascinate me. I have written a few in x86 assembly language, here’s one of them. I have tried to make the inner workings of the program as accessible — or, at least as thought-provoking — as possible even if assembly wasn’t their weapon of choice.

I’ve included the original DOS intro (you can use DOSBox to run it, it should also work on Windows XP) and a Win32 port of it, written in C while trying to keep the original structure intact. I’ll also try to explain the general idea of the effect in pseudocode where syntax can be an obstacle. The archive rubba_c.zip contains the source code, rubba_b.exe which is the compiled Win32 executable and RUBBA.COM which is the 16-bit MS-DOS executable. To compile the C program, you need the TinyPTC library (like SDL but even more bare-bones).

I won’t go into details about x86 assembly language, I suggest anyone interested first reads an introduction and learns some of the basics. However, I’ll try to make the code look interesting, explain some weird or obfuscated code and probably show some basic size optimization tricks.

### The Effect

The intro, called rubba_b, shows one effect: a twisting bar drawn using primitive light-shaded texture mapping. The color palette and the texture are generated run-time. The texturing is done using linear interpolation and no vector math is used even if the bar looks like it is rotated. The lighting is an extremely simple approximation of the light source being exactly where the camera is located. That is, the length of the textured line directly determines the light.

If looked from above, the bar will be a tower of squares. If one of the squares is rotated around the center, the corners will move in a circular pattern. So, the X coordinate will be equal to cos(corner_number*(360 degrees/4)+square_rotation), the Z coordinate (why Z? Because it goes towards the screen) is equal to the sine but it can be discarded because we will not need it for perspective calculation. Remember, we’re short on bytes.

We then modulate the bar rotation for each square in the tower. If the amount of rotation changes when moving up or down the tower, the bar will twist. If the rotation stays the same for each square, the bar will rotate uniformly and look uninteresting.

The textured horizontal line is drawn from each corner to the next corner, from left to right. If the line would be drawn from right to left, we know it isn’t facing towards the camera, another line facing the camera will be drawn over it and we simply skip the line. The color value fetched from the texture is multiplied by the line length which makes short lines darker.

Still with me?

### The Code

#### Initialization

First things first. We need to set the video mode before we continue. In the Win32 version we simply open a TinyPTC window, in the original version we need to tell BIOS to go in a nice 320x200x8 video mode (the de facto video mode back in the day).

 C asm ``ptc_open("rubba_b",320,200)`` ``````mov al,13h int 10h``````

In the above code, the Win32 part is self-explanatory. The assembly bit probably needs some clarification: we put the number 13h (hex) in the 8-bit register al and we request for interrupt 10h. This is the BIOS video interrupt and since register ax (which consists of al – “low” – and ah – “high”) equals to 0013h (when the program starts, ax is always zeroed), BIOS will call the routine for changing the video mode (ah=00h) to 13h.

If above sounds complicated, don’t worry. It’s just a matter of memorization – similar to how you would memorize the function name for changing the video mode.

The next thing we need is some space for the texture, the sine table and the double buffer. In the Win32 version this is obvious, we just add a few arrays (although since TinyPTC only supports 32-bit video modes, we will also have an array for the palette). Again, in the assembly version we won’t use any fancy way to reserve memory to save some precious bytes: we simply decide certain areas of the memory will be used for whatever we want. The benefits of no memory protection and single tasking. ;)

 C asm ``````short int sinetab[SINETAB]; unsigned char palette[256*4]; unsigned char texture[256*256]; unsigned char screen[320*200];`````` ``````mov dh,80h mov gs,dx mov dh,70h mov es,dx mov dh,60h mov fs,dx``````

The assembly version basically sets the register dx to 60xxh-80xxh (we set only the high byte, i.e. dh to save bytes, thus the low byte of dx is undefined – it won’t matter) and puts the value into various segment registers (es-gs).

This makes it so that if we use the different segment registers, we can access each of the three 64 kilobyte segments as a 64 kilobyte array. E.g. the sine is in gs, thus mov ax,[gs:2] would move the second word in the sine table in ax. In C, the equivalent would be short int value=sinetab (note how the C compiler knows the fact that a word is 2 bytes but in assembly you have to take care of that yourself – all memory accesses are always by the exact location, not the array element!).

All this is because in 16-bit memory accessing you can see only 64 kilobytes at one time. You can’t have a 128 KB array, nor can you have two 64 K arrays in the same segment. It’s something comparable to having a flashlight and a big sheet of paper in a dark room; you can move the light to show different areas but you will never see more than what there is in the spotlight.

The next two parts calculate the sine table (back in the day you simply could not do trigonometric stuff real-time, even in hardware — although in the intro it’s there just for show) and set the palette. This is pretty straight-forward stuff across the two versions. The only difference is that in the Windows version we have to remember the palette has 8-bit color components and the original has 6-bit components (0..255 ~ 0..63). And of course, the Windows version simply puts the values in a palette look-up table (because 32-bit video mode doesn’t use a palette) and the original actually sets the video mode colors.

I won’t reiterate the source code for the sine table and palette change here, I think you should be able to figure it out by comparing the source code. But in theory, here’s how to change a color: first, write the color index in port 3C8h, then write the three color components in port 3C9h (hint: dx first contains 3C8h and it’s incremented to 3C9h to save bytes).

The sine routine increases the angle (st0 the topmost register on the FPU) by 2*PI/32768 (a full circle is 2*PI, the sine table has 32768 elements). You probably should check some FPU tutorial, arithmetic on the 8087 is pretty interesting due to the stack-based architecture. For example, you first push in two numbers and then do an add, which (in most cases) pops out the two values and pushes in the result.

The texture generation bit is interesting. It also was annoying to port to C thanks to the fact you have to emulate how some instructions work – there are no accurate analogies in the C standard. A big thanks goes to baze whose code I originally cannibalized for this (I think). To be honest the conversion doesn’t work 100 % accurately but does still produce a nice texture.

The algorithm uses addition, bitwise operations and other simple things to achieve complexity thanks to how processors do arithmetics. Mainly, the results from an earlier calculation is carried over to the next calculation — when an addition or a subtraction overflows, i.e. the result is too large or too small to fit in a register, the processor lets the result wrap over but sets the carry flag.

This is quite similar to how you would carry numbers when calculating with a pen and a paper. The flag affects the results unpredictably because it’s used across quite different operations; usually you would just use to to add big numbers together as in the pen and paper example.

#### The Main Loop

Here is the meat of the code. The C version has many variables that are named after registers in order to see the connection with the original code. Sometimes, as with the 8-bit registers, some code doesn’t work exactly as in the original because you can’t do things similarly in C. E.g. you can’t have two variables that also are a part of one big register similarly how al and ah form ax (well, you can with pointers or unions but that is besides the point, kind of).

##### Self Modifying Code

I use self modifying code (SMC) in a few places because it produces faster and also much simpler code. For example, if you have a variable that is changed in a few places but used by one instruction only (and the instruction performs arithmetic or otherwise accepts a constant parameter), it’s much faster to store the variable where the constant for the instruction would be. That way you don’t have to read the variable in a register and then use the register to do something.

E.g. Let’s multiply cx by the variable foo:

 Original SMC `````` push ax ; save ax mov ax,[foo] ; move variable foo in ax imul cx,ax ; multiply cx by ax pop ax ; restore ax ... mov ax,123 ; set foo ... mov [foo],ax ; ... to 123 ... foo: dw 0``` ``` `````` imul cx,123 foo equ \$-2 ... mov ax,123 ; set foo ... mov [foo],ax ; ... to 123``````

We can exploit the fact imul (signed multiplication) accepts constant multipliers. If you looked at the code with a hex editor, you’d see 123 next to the opcode. You can change the constant run-time and you do that exactly like you would change a variable: if you just define foo as the address where the constant is (the above code defines it as the last two bytes (i.e. word) of the previous instruction: in NASM, \$ is the current location and \$-2 equals the address of the previous word).

To be concluded…

09 Mar

This is my attempt at demystifying pointers, a very useful concept available in C, C++ and other programming languages that do not lack balls. Java and PHP have references, a similar feature (C++ also has this) but pointers pretty much are the same thing and for some reason pointers seem ancient, complex or downright frightening to some (or, most).

Why I’m writing this is because I feel a lot of tutorials do not explain pointers as well as I’d like them to explain, considering they are useful and not some archaic curiosity from the 1970s. I originally expressed the concern over here. This tutorial, or introduction, is aimed at C and C++ people because Java doesn’t have pointers and because other languages are generally for wussies.

Note: This is a living tutorial, i.e. you can suggest things and I’ll make it better. I’m all for a good pointer tutorial.

### What are pointers?

At first, let’s think about computers. A very basic computer has memory for the data and the code. In a programming language, a variable refers to a location in the memory. The location, referred by address, is something like a page number in a book. At the very least, a programming language automatically assigns these memory locations and acts as a directory for the book, allowing you to refer to the locations by the variable name instead of the page number.

In a programming language, pointer is a variable that contains the address. Simple as that. In most cases, a pointer contains an address of another variable. The pointer can then be used to read or change the contents of the referred variable.

### How do I use pointers?

In C, using pointers is easy. To make a variable as a pointer to another variable of type A, you define the pointer to be of type A*.

``````A variable=42;
A* pointer_to_a_variable=&variable;
*pointer_to_a_variable=0;``````

& can be read as “address of”. * can be read as “contents of”. Try reading the above example like that and you should figure out what it does.

``````A variable=42;
A* pointer_to_a_variable=&variable; // a pointer to the above variable
*pointer_to_a_variable=0; // the contents of the variable referred are zeroed``````

### When should I use pointers?

A pointer can be very useful in a variety of situations. The simplest use would be something like passing a reference to a variable as a function parameter and the function then changes the variable. This is impossible without either references or pointers. At the very far end of the same spectrum, pointers are the only way to implement polymorphism in C++, which is an important and useful concept in object oriented programming.

Using pointers is natural when in a function you need to use the contents of an object given as a parameter and change them. If you don’t pass a pointer or a reference to the object, you will only change the data in the copied object. For example, see the C++ function below.

``````void func(Object obj) {
obj.data=0;
}``````

This bit of code would be quite useless, considering the object obj is discarded after the function exits. The data member of the object passed as a parameter will not be modified because the object is duplicated for use in the function. The below function does the same but with the exact same object that was passed as a parameter.

``````void func(Object& obj) {
obj.data=0;
}``````

Now the object is not duplicated but obj is a direct reference to the original object. data will be zeroed. Below is yet another version of the same functionality, this time with a pointer.

``````void func(Object* obj) {
obj->data=0;
}``````

This does the same thing, only that obj is a pointer to the object. The above examples show how pointers indeed can be avoided by using references or other more modern concepts (and in some languages you are forced do it with references).

However, in C++, the above example (the one with the pointer) would also allow you to pass an object as a parameter that was inherited from the class Object. By using C++ references this is not possible. Now, if the function called a member function, it would call different bits of code depending on the object! This can be a very, very powerful and nice feature. And you can’t do it without pointers (in C++).

### Show me more examples.

In C and C++, the line between arrays and pointers is a bit blurred. For example, you can use a pointer as an array, in that you can use an index with an array and a pointer. Below is an example:

``````int a={123,456};
int* b=a;
a=0;
b=0;``````

The array a is now full of zeros. Note that b only refers to the array, it doesn’t have its own data. Also note that specifying an index eliminates the asterisk in front of b. This is because in effect b is the same as *(b+1).

Continuing from the above:

``````int a={123,456};
int* b=a;
a=0;
b=b+1;
*b=0;``````

This also zeros the array, because the pointer b is incremented. A pointer is simply a variable with an address and you of course can change that value. This is called pointer arithmetic. It can be very helpful and actually will often allow faster code: using an index needs a multiplication while smartly incrementing pointers only needs an addition (your mileage may vary — compilers are getting better and better). See the below example:

``````int a;
int i;
for (i=0;i<100;i++) a[i]=0;``````

The below modification should be marginally faster.

``````int a;
int *ptr=a;
int i;
for (i=0;i<100;i++) { *ptr=0; ++ptr; }``````

This is because in effect, the below example only increments the pointer while the above calculates the address using index.

Note: Many younger coders suffer of premature optimization. This is not a permanent condition and will reduce with some experience. The above is not a good place to optimize (what computer can’t do that fast enough for 100 times?) but do keep that trick in mind. At the very least, it can make code a bit shorter.

### Afterword

This is it for now, I can’t think of anything more about basic pointer use. Honestly, it is a bit hard to imagine things from the perspective of an absolute beginner. Any ideas are welcome.