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.