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.
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)
| Group | Codes | How this page uses it |
|---|---|---|
| Motion (Group 01) | G00, G01, G02, G03 | Set explicitly at each move or inherited |
| Plane Selection | G17, G18, G19 | Set in startup block |
| Units | G20, G21 | Set in startup block |
| Distance Mode | G90, G91 | Set in startup block |
| Cutter Comp | G40, G41, G42 | G40 = off |
| Tool Length Offset | G43, G49 | G49 = off |
| Drilling Cycles (Group 09) | G80, G81, G82, G83, G84, G85 | G80 = cycle off |
| Feed Mode | G94, G95 | G94 = 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.
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
| Block | What happens | Newly active modes |
|---|---|---|
| N10 | Select work offset G54 | G54 |
| N20 | Tool change to tool 1 | Tool 1 in spindle |
| N30 | Spindle CW at 1500 RPM | M03, S1500 |
| N40 | Rapid Z50 with tool length offset H01; coolant on | G43 H01, M08 (G49 now off) |
| N50 | Rapid XY to (10,10) at Z50 | G00 |
| N60 | Drill cycle at (10,10), R2, Z-15, F120, retract to initial plane | G98, G81, F120 |
| N70 | Bare XY to (50,10); G81 repeats drill | (inherits G81) |
| N80 | Bare XY to (50,50); G81 repeats drill | (inherits G81) |
| N90 | Bare XY to (10,50); G81 repeats drill | (inherits G81) |
| N100 | Cancel drilling cycle | G80 (G81 off) |
| N110 | Rapid Z50; coolant off | M09 |
| N120 | Spindle stop | M05 |
| N130 | Program 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
- Forgetting G80 after drilling — the cycle continues on the next bare X/Y block, drilling an unexpected hole.
- Assuming G01 resets each line — it does not. Modal persists until replaced.
- Forgetting G40 after cutter compensation — the offset stays active and affects the next contour. Cancel it on a proper lead-out path, not at an arbitrary position.
- Mixing G90/G91 without tracking — switching modes mid-program without resetting causes coordinate errors.
- Assuming the startup block resets everything — it establishes the listed modes only; feed mode, work offset, spindle, and coolant are set by later lines.
- Leaving G96 CSS active on a lathe when you switch to a finishing tool — CSS is modal and will change RPM as the diameter changes; if you expected a fixed RPM, the surface speed will surprise you.
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
- Haas G80 Canned Cycle Cancel — supports cycle cancellation behavior.
- Haas M30 Program End — supports program end behavior.
- Program examples and speeds/feeds are classroom teaching assumptions; verify against tooling catalogs and machine documentation.