← Back to CNC Programming

Modal State and Code Persistence

Modal codes are one of the most important and least understood concepts in G-code. This page explains what modal means, how modal groups work, why bare X/Y moves after a drilling cycle drill extra holes, and how to track machine state block by block.

Concept

A modal code stays active after it is commanded. Once G01 is on, every motion block remains G01 until G00, G02, or G03 replaces it. A non-modal code (like G53 or G04 dwell) is active for one block only and does not persist.

Modal codes are organized into groups. Within a group, only one code can be active at a time. Motion codes (G00/G01/G02/G03) are one group. Plane selection (G17/G18/G19) is another. Drilling cycles (G81–G85) are a separate group from motion. Codes from different groups coexist — G01 does not cancel G21, and G81 does not cancel G90.

Modal groups (one active per group) Motion: G00 G01 G02 G03 Plane: G17 G18 G19 Units: G20 G21 Distance: G90 G91 Cycles: G80–G89 Feed: G94 G95 Same group: new code replaces old. Different groups: both stay active together. Note: G21/G17/G90 on G01 X50 F200 are assumed entry state, not derived from that line.

Why It Matters

Missing G80 after a drilling cycle means the cycle stays active, and the next bare X/Y block drills another hole. This is how programs drill holes at unexpected positions. Modal state errors are silent: the code looks fine, but the machine executes a different mode than you intended. Understanding modal groups is how you predict what the machine will do, not just what each line says.

When you read a program, you are tracking a state machine. After the startup block, the controller is in a specific combination of modes. Each line may change one or more of those modes, and the next line inherits everything the previous line set. A programmer who does not track this will be surprised when a hole appears where none was programmed.

How It Works

Common Modal Groups (teaching selection)

GroupCodesHow this page uses it
Motion (Group 01)G00, G01, G02, G03Set explicitly at each move or inherited
Plane SelectionG17, G18, G19Set in startup block
UnitsG20, G21Set in startup block
Distance ModeG90, G91Set in startup block
Cutter CompG40, G41, G42G40 = off
Tool Length OffsetG43, G49G49 = off
Drilling Cycles (Group 09)G80, G81, G82, G83, G84, G85G80 = cycle off
Feed ModeG94, G95G94 = feed per minute

Exact group membership depends on the controller and software version. This page explicitly sets the modes it needs rather than relying on defaults. F and S are modal values (not G-code group replacements), and T selects a tool; they persist until changed.

Simple Inheritance Example

G01 X10 F200
X50        (still G01, still F200 — no need to repeat)
Y30        (still G01, still F200)
G00 X0 Y0  (now rapid — G01 cancelled by G00)

Lines 2 and 3 inherit G01 and F200 from line 1. If the second line had been intended as rapid, you must explicitly write G00. The machine does not reset to G00 after each block.

The Startup Block Establishes Known State

This is why every program starts with: G21 G17 G90 G40 G49 G80. This block explicitly establishes the listed modes — metric units, XY plane, absolute coordinates, cutter compensation off, tool length compensation off, and drilling cycles off. It does not set feed mode, work offset, spindle speed, or coolant; those are set by later lines. The point is that before any tool moves, the programmer has declared the state they care about, rather than inheriting whatever the previous program left behind.

Example

Forgotten G80 on Haas

On a Haas mill, G00 and G01 cancel an active drilling cycle. The dangerous case is not a G00 line — it is a bare X/Y block while G81 is still active. Here is the intentional-error teaching fragment (entry state: G21 G17 G90 G94 active; tool, work offset, spindle, and clearance verified):

G00 Z50.
G00 X10. Y10.
G98 G81 X10. Y10. R2. Z-15. F100.   (drills hole 1 at 10,10)
X50. Y10.                            (drills hole 2 at 50,10)
X50. Y50.                            (drills hole 3 at 50,50)
X0. Y0.    (INTENTIONAL ERROR: G81 still active, drills hole 4 at 0,0)
G80                                   (cancel cycle)
G00 Z50.

Each bare X/Y block after the G81 line repeats the drill cycle at the new position. The fix is to write G80 after the last hole, before any other Z motion. On Haas, G00 or G01 also cancels the cycle, but writing G80 explicitly makes the program's intent clear and does not depend on the reader knowing which controls cancel on G00 and which do not.

Forgotten G80: 3 intended holes + 1 unintended Hole 1 (10,10) Hole 2 (50,10) Hole 3 (50,50) UNWANTED (0,0) Y increases upward; X increases rightward

Full Program State Trace

Trace this short program and track the active modes. Entry state: G21 G17 G90 G94 already active; tool, work offset, spindle, and clearance verified.

N10 G54
N20 T01 M06
N30 S1500 M03
N40 G00 G43 H01 Z50. M08
N50 G00 X10. Y10.
N60 G98 G81 R2. Z-15. F120.
N70 X50. Y10.
N80 X50. Y50.
N90 X10. Y50.
N100 G80
N110 G00 Z50. M09
N120 M05
N130 M30
BlockWhat happensNewly active modes
N10Select work offset G54G54
N20Tool change to tool 1Tool 1 in spindle
N30Spindle CW at 1500 RPMM03, S1500
N40Rapid Z50 with tool length offset H01; coolant onG43 H01, M08 (G49 now off)
N50Rapid XY to (10,10) at Z50G00
N60Drill cycle at (10,10), R2, Z-15, F120, retract to initial planeG98, G81, F120
N70Bare XY to (50,10); G81 repeats drill(inherits G81)
N80Bare XY to (50,50); G81 repeats drill(inherits G81)
N90Bare XY to (10,50); G81 repeats drill(inherits G81)
N100Cancel drilling cycleG80 (G81 off)
N110Rapid Z50; coolant offM09
N120Spindle stopM05
N130Program end and rewind—

Four drill events occur: at (10,10), (50,10), (50,50), and (10,50). On the Haas mill used here, G81 belongs to Group 09 and G00/G01 to Group 01. Despite belonging to different groups, G00 and G01 cancel the canned cycle. G80 explicitly cancels it.

Modal State Across Program Boundaries

When the program ends with M30, the controller stops the program and rewinds. Whether spindle, coolant, and modal G-codes reset depends on the controller and its settings; consult the machine documentation. On Haas, M30 ends the program and the spindle and coolant are stopped as part of program end. Writing M09 and M05 explicitly before M30 is good practice for readability and process clarity, not because the next program would otherwise continue cutting.

Subprograms and Modal State

When you call a subprogram with M98, modal state does not reset. G-codes set inside the subprogram persist after M99 returns to the main program. This is why the rule "always return to G90 after G91 in a subprogram" matters: if the subprogram uses G91 for incremental moves and does not restore G90, the main program resumes in incremental mode. The same applies to cutter compensation, cycles, and feed mode. A well-written subprogram restores the modes it changed, or the main program re-establishes them after the call.

Reading State Back on the Controller

Modern controllers have a "current commands" or "modal display" page that shows every active G-code group. When you are debugging a program that is not doing what you expect, this page is the first place to look. It tells you whether the controller thinks it is in G90 or G91, G20 or G21, G81 or G80, G94 or G95. If the program looks correct but the machine moves oddly, the modal display almost always reveals the problem — usually a leftover mode you did not know was active.

Modal Groups in Detail

Each modal group represents a single machine setting. The motion group (G00/G01/G02/G03) determines how the next axis move is interpolated. The plane group (G17/G18/G19) determines which two axes define the plane for circular interpolation and cutter compensation. The distance group (G90/G91) determines whether coordinates are absolute or incremental. The units group (G20/G21) determines inch vs millimeter. Each group is independent: setting G90 does not change the plane, the units, or the feed mode.

When a new code from a group appears, it replaces the previous code in that group. For example, G02 replaces G01 in the motion group. It does not affect G17, G90, or G21. This independence is what lets you write a line like G90 G01 X50 F200: G90 and G01 come from different G-code groups, and F200 is a held numeric feed value, not a third G-code group. All three are active together.

Non-Modal (One-Shot) Codes

Not every G-code is modal. Non-modal codes act on the block they appear in and then stop. G04 (dwell) is the classic example: G04 P1000 pauses for one second and then the program continues. The dwell does not persist. G53 (machine coordinate selection) is also one-shot: it applies only to the block it is written in. If you need machine coordinates again, you must write G53 again. Recognizing whether a code is modal or one-shot prevents the mistake of expecting a one-shot code to persist.

Feed and Spindle as Modal Values

Feed F and spindle S are modal numeric values, not G-code group replacements. Once you write F300, every subsequent G01/G02/G03 block inherits 300 until you write a new F. The same is true for S. You do not need to repeat F on every cutting line — you write it once at the start of a cutting section and change it only when the feed changes. If the program has already set F (e.g. G94 F200) and the feed mode has not changed, later G01 blocks may use that F. Before the first cut, confirm the feed value and mode rather than relying on unknown entry state. Example trace: G94 F200 (set feed mode and value) → G00 X0 Y0 (rapid, no feed used) → G01 Z-2. F200 (cut at 200 mm/min; F200 restated for clarity).

Modal Errors in Practice

The most common modal error in production is leaving a drilling cycle active. Two branches: (A) after G81 you write G00 Z50 — on Haas this cancels the cycle, so a following bare X/Y block is a rapid move. (B) after G81 you write a bare X/Y block directly — G81 repeats the drill at that new position. Writing G80 explicitly after the last hole removes this ambiguity and makes intent clear regardless of which controller is used.

The second most common error is leaving cutter compensation active. G41/G42 must be cancelled on a proper lead-out path — a straight move away from the part before switching to another operation. Cancelling compensation at an arbitrary position can cause the tool to shift sideways unexpectedly. The lead-out and lead-in moves are part of the compensation logic, not optional cleanup.

The third common error is a leftover G91 subprogram. A subprogram that uses incremental moves for a bolt pattern must restore G90 before returning. If it does not, the main program resumes in incremental mode and every subsequent coordinate line moves by the stated amount rather than to the stated position. This produces parts whose features drift further and further from the drawing.

Common Mistakes

Practice

1. After G81 R2. Z-15. F100, you write X30 Y20. What happens?

Show answer

The machine moves to (30,20) and drills another hole at R2 Z-15 F100. G81 is modal. You need G80 to cancel the cycle.

2. What cancels G41/G42?

Show answer

G40 cancels cutter compensation. It must be explicitly programmed on a proper lead-out path — the machine does not turn it off automatically.

3. What does the startup block actually establish?

Show answer

It explicitly sets the listed modes (metric, XY plane, absolute, comp off, cycles off). It does not set feed mode, work offset, spindle, or coolant — those are set by later lines.

4. After G01 X50 F200, you write Y30. What mode and feed are active?

Show answer

G01 at F200 — both are modal and inherited. Y30 moves to Y30 in a straight line at 200 mm/min.

5. You see G90 G54 G00 X0 Y0 on one line. Which modal groups are explicitly set on this line?

Show answer

Distance mode (G90), work offset (G54), and motion (G00) are explicitly selected on that line. Because G00 is executed, any active drilling cycle is cancelled on this Haas mill. Units, plane, and compensation are inherited from earlier lines and must be checked.

Sources