M98 and M99 — Subprograms in CNC Milling
A subprogram is a separate G-code program that can be called from the main program, executed, and returned from — similar to a function call in software. M98 calls the subprogram; M99 at the end of the subprogram returns execution to the main program.
Subprograms are most useful when the same machining sequence repeats at multiple locations, or when a complex operation is cleaner to isolate in its own program block.
M98 — Call Subprogram
M98 P_ (call subprogram with program number P)
M98 P_ L_ (call subprogram P, repeat L times)
The P word specifies the program number of the subprogram. On Fanuc-style controllers, subprograms are stored as separate O-numbered programs.
M98 P1000 (call O1000 once)
M98 P1000 L4 (call O1000 four times)
On some controllers, P encodes both the repetition count and the program number in a single value (e.g., P41000 = call O1000, 4 times). Check your controller manual for the format used.
M99 — Return from Subprogram
M99 placed at the end of a subprogram returns execution to the main program at the block immediately after the M98 call.
%
O1000 (SUBPROGRAM)
G91 (incremental — moves are relative)
G0 Z-5.
G1 X50. F400
G0 Z5.
M99 (return to main program)
%
Complete Example — Four Identical Pockets
A part has four identical rectangular pockets at different locations. Instead of programming the pocket geometry four times, write it once as a subprogram and call it from each location.
Main program:
%
O0100 (MAIN PROGRAM — 4 POCKETS)
G17 G21 G40 G49 G80 G90
G28 G91 Z0.
G90
T1 M6 (10mm end mill)
G0 G90 G54 S4000 M3
G43 H1 Z50.
M8
G0 X10. Y10. (position to pocket 1 origin)
M98 P2000 (call pocket subprogram)
G0 X60. Y10. (position to pocket 2 origin)
M98 P2000
G0 X10. Y60. (position to pocket 3 origin)
M98 P2000
G0 X60. Y60. (position to pocket 4 origin)
M98 P2000
G0 Z50.
M9 M5
G28 G91 Z0.
G28 X0. Y0.
M30
%
Subprogram O2000:
%
O2000 (POCKET SUBPROGRAM — 30×20mm, 5mm deep)
G91 (incremental — all moves relative to call position)
G0 Z-47. (down from Z50 to Z3 above part)
G1 Z-8. F200 (plunge to -5mm depth from part surface)
G1 X30. F500 (cut pocket)
Y20.
X-30.
Y-20.
G0 Z55. (retract back to Z50)
G90 (restore absolute)
M99 (return to main program)
%
The subprogram uses incremental moves (G91) so it operates relative to wherever the main program positioned the tool before calling it. This makes the subprogram reusable at any XY location.
Nested Subprograms
A subprogram can call another subprogram. Most controllers support 2–4 levels of nesting.
O0100 (main)
M98 P0200 (call level 1 sub)
M30
O0200 (level 1 sub)
M98 P0300 (call level 2 sub)
M99
O0300 (level 2 sub)
(machining operations)
M99
Check your controller manual for the maximum nesting depth — exceeding it causes a program error.
M99 in the Main Program — Infinite Loop
When M99 appears in the main program (not a subprogram), it rewinds execution to the beginning of the program and repeats it. This creates an infinite loop — useful on some dedicated production machines that run a single program continuously until the operator intervenes.
O0100
(machining program)
M99 (restart from beginning — loops indefinitely)
Do not confuse this with M30 (end and rewind, requires Cycle Start to restart) — M99 in the main program restarts automatically without operator input.
Local Subprograms (Fanuc M97)
Some Fanuc controllers support M97 for local subprogram calls — the subprogram is embedded within the same program file rather than stored as a separate O number:
O0100
(main program)
M97 P1000 L3 (jump to N1000 label, 3 times)
M30
N1000 (local subprogram)
G91
G1 X20. F300
G90
M99
Local subprograms avoid the need to manage separate program files, which can be useful on machines with limited memory or when the subprogram is only used in one context.
When to Use Subprograms
Subprograms are most valuable when:
- The same feature (pocket, hole pattern, profile) repeats at multiple locations
- A complex multi-tool sequence is used on a pattern of identical parts
- The program is long enough that repetition makes it difficult to read and maintain
- You want to test a machining sequence in isolation before integrating it into a larger program
For simple hole patterns at equal spacing, canned cycles with incremental positioning are often faster to write than subprograms. Subprograms become essential when the repeated feature involves multiple tools and operations.
Simulate Subprogram Calls
The interaction between main program positioning and subprogram incremental moves is easy to get wrong — a single sign error in G91 inside the subprogram moves the tool in the wrong direction. Eureka3X expands and simulates the full subprogram call sequence, showing the actual tool path for every call iteration on the 3D machine model.
Try Eureka3X free for 30 days →