Befunge Techniques

Subroutines in a Grid Language

Befunge has no call instruction, so reusable routines are emulated. Compare copying code, dispatching a return with a stack value, and rewriting cells.

On this page
  1. The problem
  2. Approach 1: copy the code
  3. Approach 2: share the routine and dispatch the return
  4. Approach 3: rewrite the exit
  5. Choosing between them
  6. Reducing the pain

The problem

A subroutine is code you write once and use from several places. Most languages do this with a call instruction that remembers where you came from, and a return that goes back. Befunge has neither. The pointer travels in straight lines and turns only when it meets an arrow. It has no memory of its route, and no instruction that means “go back to where I was”.

Reuse is still possible, but you have to arrange it. There are three main approaches, with different costs.

Approach 1: copy the code

The plainest method is to write the routine again wherever it is needed. For a routine of a few cells, such as printing a single separator, this is often the best choice. It costs grid space and creates the risk that a fix applied to one copy is forgotten in another, but it needs no extra machinery. Because the playfield in Befunge-93 is limited, copying becomes expensive for anything large, which is one of the Befunge-93 limits that nudges larger programs toward other designs.

Approach 2: share the routine and dispatch the return

For anything larger, put the routine in one place and let each caller send the pointer into it. The hard part is leaving. Since the routine cannot know which caller used it, the caller must leave a clue, and the routine uses the clue at the end to pick a path.

The usual clue is a value on the stack. The caller pushes a small number before entering, the routine does its work without disturbing that value, and at the end it pops the number and uses a horizontal or vertical if to choose the exit. With two callers, one if is enough. With more, you chain several tests.

Here is a complete small example with two callers. The routine prints an asterisk. The first caller then prints a and calls the routine again, and the second caller prints b and stops.

1 v
  v,"a"0<
  >67*, |
        >"b",@

Follow it through. The first cell pushes 1, and the v on row 0 sends the pointer down. The v on row 1 sends it down again, to the > on row 2, which is the entrance to the routine. The routine pushes 6 and 7, multiplies to get 42, and prints it with a comma: an asterisk. Then the | pops the 1 that was left earlier. It is nonzero, so the pointer goes up to the < on row 1.

Moving west, the pointer pushes 0, enters string mode, pushes the code for a, leaves string mode, and prints a. The 0 remains on the stack. It reaches the v on row 1 again, goes down, and re-enters the routine, which prints another asterisk. This time the | pops 0, which is zero, so the pointer goes down to the > in row 3. That prints b and ends. The output is *a*b.

The one idea to take is that the return address is represented as data. The numbers 1 and 0 stand for “return to the first caller” and “return to the second”. In a larger program they might stand for many callers.

Some rules make this work reliably:

  • The routine must leave the clue on top of the stack when it finishes. If it needs working values, it should clean them up first or keep them beneath the clue.
  • Arguments and results also travel on the stack, so agree on an order. A common layout is arguments below the clue and results replacing the arguments.
  • Both exits of the dispatch must rejoin the callers at cells the pointer can reach cleanly.

Approach 3: rewrite the exit

The third way is to change the grid itself. Before entering the routine, the caller uses the put instruction to place an arrow at the routine’s last cell. The arrow points back toward that caller. The routine ends on that arrow and goes home. This is a form of self-modifying code, and it works with any number of callers without chains of tests.

It is also harder to reason about. A reader can no longer tell where the exit goes by looking at the grid, because the exit cell changes while the program runs. Tracing by hand has to include the current contents of the modified cell. If you use this approach, put a note nearby, in cells that never run, saying which cell is rewritten and by whom.

Choosing between them

Approach Best for Main cost
Copying Tiny routines Space and duplicate fixes
Stack dispatch A few callers Extra tests per caller
Rewriting the exit Many callers Hard to read and trace

For most programs, a mix is sensible: copy the tiny helpers, share one or two bigger routines with stack dispatch, and avoid rewriting unless the program really needs it.

Reducing the pain

Some habits help whichever you pick.

  • Keep each routine in its own region of the grid with one entrance and one exit.
  • Document the stack effect of each routine in a note outside the program: what it expects, what it leaves.
  • Test a routine alone, with a short driver program, before connecting it.
  • Be careful with recursion. Because the clue is on the stack, a routine that calls itself must push a fresh clue each time, and you must make sure the stack does not grow without bound.

If a routine seems to hang or to return to the wrong place, step through it with the stack written out at each step. Return bugs nearly always show up as an unexpected value where the dispatch expects a clue, or as an empty stack at the dispatch, which is the stack underflow problem under another name.

The complete guide to befunge techniques

Befunge Programming Techniques Overview
Read the guide

Keep exploring

Guides from other topics

All topics →

What Is Funge-98?

Funge-98 generalizes Befunge into a larger, better-defined family of grid languages. Learn its goals, its main differences from Befunge-93, and why it matters.

Funge-98 5 min read