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 P1000calls programO1000.L4repeats it four times.M99returns. - Haas:
M97 P100jumps to lineN100in the same file and runs toM99. - LinuxCNC:
o100 sub…o100 endsubdefines it, ando100 callruns 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.
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.
Part of the guide: How to estimate CNC machining time from G-code
The pattern inside the pattern: Peck drilling and cycle time: what each hole really costs.
The overhead between calls: Non-cutting time: rapids, dwells and tool changes.