Learn

Subprograms and cycle time: M98, M97 and o-words

Write a pattern once. Call it a hundred times. Subprograms are how production G-code stays short, and where nearly all of its cycle time hides.

One pattern, many calls

A subprogram is a block of G-code you write once and call whenever you need it. Drill pattern, contour, probing move, anything you’d otherwise copy-paste. The main program becomes a short list of calls. The subprograms do the work.

Three dialects, same idea:

  • Fanuc: M98 P1000 calls program O1000. L4 repeats it four times. M99 returns.
  • Haas: M97 P100 jumps to line N100 in the same file and runs to M99.
  • LinuxCNC: o100 subo100 endsub defines it, and o100 call runs it.

Calls nest, too. A main calls a fixture routine, the fixture routine calls a hole pattern. Clean, compact, standard.

The classic layout: a tombstone job

Here’s the shape of thousands of real production programs. One drill pattern, four fixtures:

O0001 (MAIN)
G54 M98 P1000   (fixture 1)
G55 M98 P1000   (fixture 2)
G56 M98 P1000   (fixture 3)
G57 M98 P1000   (fixture 4)
M30

O1000 (12-HOLE PATTERN)
G81 X0 Y0 Z-6 R2 F250
X8
…
G80
M99

Twelve holes, written once. Forty-eight holes drilled. The main program is six lines long.

Where the cycle time lives

Look at that main program again. It contains almost no motion. The motion, and the minutes, are inside O1000, multiplied by four. Repeat counts multiply it further: M98 P1000 L20 is twenty full runs of the pattern from one line of code.

So a cycle time estimate has one job here: run the calls. Every call, every repeat, every nested level. That’s the whole program.

Simulate it, calls and all

UltraNC expands subprograms inline: M98 with L and packed-P repeat counts, Haas M97 local blocks, and LinuxCNC o-word calls, nested included. Every repetition lands on the clock, next to the tool changes, dwells and rapids it sits between. If a call can’t be resolved (a missing target, a macro that needs variables) the viewer names it in a per-file note, so you always know what the number covers.

Try it now: pick Tombstone / Subprograms from the demo menu in the viewer. Watch the same twelve-hole pattern run four times, and read the total.

Open the viewer →

Common questions

What does M98 do in G-code?

M98 calls a subprogram. M98 P1000 runs the program numbered O1000, then returns to the line after the call. Add a repeat: M98 P1000 L4 runs it four times. Fanuc also packs the count into P: M98 P41000 means "run O1000 four times". The subprogram ends with M99, which returns to the caller.

What is the difference between M97 and M98?

M98 calls a separate program by O-number. M97 is Haas's local version: it jumps to an N-numbered line inside the same file, usually placed after M30, and runs until M99. Same idea, write a block once and call it many times, with the whole job kept in one file.

How do I estimate cycle time for a program with subprograms?

Simulate it in a tool that expands the calls. The main program of a subprogram-heavy job is mostly call lines. Nearly all the real motion lives inside the subs. Multiply that by repeat counts and fixtures, and an estimate that skips the calls misses most of the cycle. UltraNC expands M98, M97 and o-word calls inline, nested included, and puts every repetition on the clock.

Why is my program's code written after M30?

That's the standard Fanuc layout. The main program ends at M30; subprogram definitions (O1000, O2000 …) live after it in the same file. They only execute when called. It looks odd the first time, and it's completely normal.

← Back to Learn