Funge-98 in Depth

Storage Offset and Relocatable Code

How the Funge-98 storage offset shifts the origin used by get and put, and how that lets one routine and its data work wherever it is placed on the grid.

On this page
  1. The problem it solves
  2. How the offset works
  3. A mental picture
  4. A relocatable counter
  5. Benefits
  6. Pitfalls
  7. Choosing when to use it

The problem it solves

The get and put instructions read and write cells by coordinates. In the simplest programs those coordinates are fixed numbers: store a counter at column 0, row 10, and read it from there later. That works until you want to reuse the same code in a second place, say to keep two independent counters. The code would need a different set of numbers for each copy, and you would end up writing nearly identical routines.

Funge-98 offers a neat fix. Each pointer carries a storage offset, a vector that is added to the coordinates used by get and put. By changing the offset, the same instructions address a different region without any change to the code.

How the offset works

The effect is straightforward. If the offset is (10, 5) and a program puts a value at (2, 0), the value lands at (12, 5). Reading with get applies the same shift. By default the offset is the origin, (0, 0), so a program that never touches it behaves as if the feature did not exist.

The offset only affects get and put. It does not change where the pointer travels, where the program text was loaded or the position reported by other instructions. That is why the idea is about relocatable data, not about moving the code itself. Still, with a small amount of design the data and code can be treated as one unit.

The offset is normally set by the block-begin instruction, written {. This instruction opens a new stack on the stack of stacks and sets the storage offset to the position just after itself, in the direction of travel. The block-end instruction, }, restores the previous offset. This pairing means you rarely need to compute an offset by hand: the cell after the opening brace becomes the local origin for everything inside the block. The stack side of these instructions is covered in the article on working with the stack of stacks.

A mental picture

Imagine a sheet of graph paper with a transparent overlay. The program text is printed on the sheet. The overlay carries your variables, and you can slide it around. Get and put always talk to the overlay. Sliding it to a new spot gives you a fresh set of variables, with the same names (coordinates) as before.

Now imagine two copies of the same routine, one placed in the upper part of the grid and one in the lower. Each opens a block just before its variable area. Inside each block, “cell (0, 1)” means one row below the opening brace of that particular copy. Both copies use identical numbers, but each touches its own cells.

A relocatable counter

Here is the idea in words, without the details of a specific layout:

  1. The routine begins with a block-begin instruction, so the offset points at a small area to its right or below.
  2. It uses put to store a starting value at relative position (0, 0), then get and put at that same position to read, change and write it back.
  3. It finishes with block-end, which restores the caller’s offset.

Every copy of that routine, wherever it sits, has its own private counter. A caller that wants two counters simply sends the pointer into two different copies. Because the routine says nothing about absolute positions, it can be pasted into any open area of the grid. That is what makes it relocatable.

Benefits

  • Reuse without renumbering. You can copy a routine without editing every coordinate.
  • Private working space. Local data stays near its code, which is easier to read than a table of numbers far away.
  • Cleaner recursion-like patterns. Each activation can use its own region, so an inner activation does not erase the outer one’s data, provided each is placed in separate space.
  • Fewer collisions. Coordinates are relative, so two unrelated parts of a program cannot accidentally reuse the same absolute cell.

This is a natural companion to the ideas in grid variables and grid subroutines, and it fits the same discipline: decide where each piece of data lives, and keep that decision in one place.

Pitfalls

Forgetting the offset exists. A common source of confusion is a value that seems to vanish. If you put at (3, 0) and then check absolute (3, 0) with the offset set elsewhere, you will not find it. When debugging, print or inspect the current offset first.

Unbalanced blocks. If you enter a block and never leave it, the offset stays shifted, and later code looks in the wrong place. Likewise, leaving more blocks than you entered is an error, and the instruction typically reflects.

Overwriting code. Relative coordinates can still reach cells that hold instructions. A put with the wrong sign or a bigger number than expected can silently overwrite the program. This turns into self-modifying code by accident, and it is a frequent cause of programs that run once and then fail.

Dimensions. The offset has as many components as the space has dimensions. Code written for two dimensions needs adjusting for others, and the number of values to push for a position changes accordingly.

Interpreter differences. The exact timing of when the offset changes is part of the specification, but mistakes in interpreters exist. Test a simple block on any new interpreter.

Choosing when to use it

If a program has a single set of variables, absolute coordinates are fine and easier to read. The offset pays off when you have several instances of the same logic, when you want to keep a routine’s data next to its code, or when you are building a library of pieces to paste into larger programs. As a habit, give every reusable piece a clear data region with a short note describing its layout, such as which relative cell is the counter and which is the limit, and keep it away from the code cells. The offset then does the rest of the work for you.

The complete guide to funge-98 in depth

Funge-98 Features in Depth
Read the guide

Keep exploring

Guides from other topics

All topics →