Haas Programming & Macros
If you can program a Fanuc, you can program a Haas — the control was designed that way on purpose. Programs use O-numbers, offsets live in G54–G59, macros are Fanuc-style #-variables called with G65, and IF/GOTO/WHILE read exactly the way you already write them. But Haas is a dialect, not a clone: it has its own way of expanding work offsets (G154), its own in-program subroutine call (M97), a friendlier tool change, and a family of settings that let the control impersonate Fanuc or Yasnac behavior where the brands historically disagreed. This page maps the differences, then gets you writing macros on a Haas.
Haas Speaks Fanuc — Mostly
One paragraph of control history is worth having before the details. The Classic Haas Control (CHC) is the beige-keypad control that shipped on Haas machines for roughly two decades; the Next Generation Control (NGC) is the current touchscreen-era control found on machines built since the mid-2010s. Programming is deliberately continuous between them — a program written for a CHC mill almost always runs on NGC unchanged — but the NGC adds a larger retained macro-variable range (the #10000s), better file handling (USB, Net Share), and modern output routing for DPRNT. Everything on this page applies to NGC; where the CHC differs, it’s called out.
What carries over from Fanuc unchanged: block structure, G0/G1/G2/G3, G17/G18/G19, G90/G91, G43 H tool length compensation, G41/G42 D cutter comp, M98/M99 subprogram calls, canned cycles G81–G89 with G98/G99 return planes, and the whole Macro-B layer — #-variables, square-bracket math in degrees, G65 argument passing, WHILE [..] DOn. Your Fanuc habits are assets here. The rest of this page is about the places they’ll steer you slightly wrong.
Where Haas Differs from Fanuc
These are the differences you’ll actually hit in the first week of running a Haas with Fanuc muscle memory:
| Topic | Fanuc habit | How Haas does it |
|---|---|---|
| Extra work offsets | G54.1 P1–P48 (or P300) | G154 P1–P99 — 99 additional offsets beyond G54–G59. Legacy programs may use G110–G129, which are aliases for G154 P1–P20. A Fanuc post that emits G54.1 P1 needs to be re-posted as G154 P1. |
| Local subprogram call | M98 P8001 — always a separate program | M97 P100 L4 calls line N100 inside the same program (ends with M99), repeating L times. One file holds the whole job — no separate O-number to lose. M98 P still works for separate programs, just like Fanuc. |
| Tool change | G91 G28 Z0 before M06; T-word conventions vary by builder | Tn M06 is enough — the control retracts Z to the tool-change position itself. A bare Tn earlier in the program pre-stages the next tool on side-mount changers so the swap is instant. The Fanuc-style home-then-change still runs; it’s just wasted motion. |
| G52 / G92 behavior | Fixed by the control model | Selectable. Setting 33 (COORDINATE SYSTEM) switches the control between FANUC, HAAS, and YASNAC interpretations of G52/G92. Check it before trusting any program that shifts coordinates — the same block means different things in different modes. |
| Cutter comp style | Type A vs Type B fixed per control | Selectable. Setting 58 (CUTTER COMP) picks FANUC or YASNAC compensation behavior — it changes how the control handles the approach/departure moves and comp around corners. |
| Deep-hole pecking | G83 with a fixed Q peck | G83 takes Q like Fanuc, or I/J/K for variable pecking — first peck I, each subsequent peck reduced by J, never smaller than K. Retract distances for peck cycles come from settings, not program words. |
| Hole patterns | Write a macro or post every hole | Built-in pattern cycles: G70 bolt hole circle, G71 bolt hole arc, G72 bolt holes along an angle — used with an active canned cycle. G12/G13 circular pocket milling is also built in. |
| Program stop messaging | #3000/#3006 | Same variables work, and Haas comments in parentheses display on screen — operators actually see M00 (FLIP PART). |
The two settings rows deserve emphasis: on a Haas, coordinate-shift and cutter-comp semantics are configuration, not constants. When a program behaves differently on two Haas machines, Settings 33 and 58 are among the first things to compare.
M97 in Practice: One-File Jobs
M97 deserves its own example because it changes how you organize programs. On a Fanuc, a repeated operation means a second program in memory — and a second thing to forget to transfer, rename, or update. On a Haas, the subroutine lives at an N-number at the bottom of the same file, after M30, where the main body can never fall into it:
O00200 (SAME PART IN FOUR VISES - ONE FILE)
T2 M06 (SPOT DRILL)
G90 G17 G00 G54 X0 Y0 (FIRST VISE = G54)
G43 H02 Z1.0 S4000 M03
G54 M97 P1000 (RUN THE PATTERN IN VISE 1)
G55 M97 P1000 (VISE 2 - SAME PATTERN, NEW OFFSET)
G56 M97 P1000 (VISE 3)
G154 P1 (VISE 4 - EXPANDED OFFSET, FANUC'S G54.1 P1)
M97 P1000 (OWN BLOCK - G154 AND M97 BOTH USE P)
G53 G00 Z0. M05 (RETRACT IN MACHINE COORDS)
M30 (END OF MAIN - SUBROUTINE BELOW IS NEVER)
(REACHED EXCEPT THROUGH M97)
N1000 (THE PATTERN - RUNS IN WHATEVER OFFSET IS ACTIVE)
G00 X0.5 Y0.5 (POSITION IN THE CURRENT WORK OFFSET)
G99 G81 Z-0.2 R0.1 F12. (SPOT HOLE 1)
X1.5 (HOLE 2)
Y1.5 (HOLE 3)
X0.5 (HOLE 4)
G80 (CANCEL CYCLE)
G00 Z1.0 (CLEAR THE PART BEFORE THE NEXT VISE)
M99 (RETURN TO THE LINE AFTER M97)
Add L to repeat — M97 P1000 L4 runs the block four times, which pairs naturally with G91 incremental shifts. The mental model: M97 P jumps to a line number in this program, M98 P jumps to a program number in memory. Both return with M99, and neither passes arguments — when you need arguments, that’s G65’s job.
Macro Basics on Haas
Macros are a purchasable option on Haas machines (enabled via option code — most machines ordered for toolroom or probing work have it, and any machine with a Renishaw/WIPS package does, because the probing cycles are themselves macros). With the option on, you get Fanuc Macro B nearly verbatim: #-variables, square-bracket arithmetic with degree-mode trig, IF [..] GOTO, WHILE [..] DOn ... ENDn, and G65 calls with letter arguments. If you’ve read Macro Control Flow, there is nothing new to learn syntactically.
The variable families, in brief — the full map with every system-variable range lives at Haas System Variables:
| Range | Class | Notes |
|---|---|---|
#1–#33 | Local | Receive G65 arguments; per call level; cleared on return. Same letter→variable map as Fanuc (A=#1, B=#2… Z=#26). |
#100–#199 | General purpose (volatile) | Scratch space shared across programs; cleared at power-off on legacy controls. |
#500–#549 | General purpose (retained) | Survive power-off — counters, calibration, saved results. Note: with a probing package installed, much of the range just above (#550+) is claimed by probe calibration data — don’t stomp on it. |
#10000s | General purpose, NGC | The NGC adds a large retained range in the #10000s — use these for new work on NGC instead of fighting over #500–#549. |
#1000 and up | System | Positions, offsets, modal state, skip results, timers — largely Fanuc-compatible numbering (work offsets at #5221+, skip positions at #5061+). |
Calling macros. G65 P9010 A15. B2.0 H6 calls program O09010 and lands A in #1, B in #2, H in #11 — identical to Fanuc, including the reserved letters (G, L, N, O, P can’t be arguments). Beyond G65, Haas supports macro-call aliasing: a block of settings maps up to ten G codes and ten M codes onto O9000-series macro programs, so your finished macro runs as a plain-looking G or M code and operators never see the plumbing. Haas’s own probing cycles work exactly this way. For the general technique see Custom G/M Cycles.
Where macros live. By convention, finished macros go in the O9000 program range, and a control setting can lock 9000-series programs against editing and viewing — which is why you usually can’t open the Renishaw probing macros on a stock machine, and why your own production macros should live there too once they’re debugged. Develop under a normal O-number, then move the program up into the protected range when it ships to the floor.
A handful of system variables cover most day-one macro work, and their numbering matches Fanuc:
| Variable | What it is |
|---|---|
#3000 | Programmable alarm — stops the machine with your message on screen |
#3006 | Program stop with message — an M00 that explains itself |
#5021–#5023 | Current machine-coordinate position (X, Y, Z) |
#5041–#5043 | Current work-coordinate position |
#5061–#5063 | Skip position — where the G31 probe signal fired |
#5221–#5226 | G54 work offset values (G55 continues at #5241, and so on) — read and write |
Control flow is Fanuc, verbatim. Conditions use the two-letter comparators (EQ NE GT LT GE LE), branching is IF [..] GOTO n, and loops are WHILE [..] DOn ... ENDn. Math uses square brackets (parentheses are comments on Haas too, and comments display on the screen), and trig works in degrees. Any Fanuc macro-logic reference — including this wiki’s Macro Control Flow and Macro Arithmetic pages — applies to Haas without translation:
IF [#101 GE 12] GOTO 500 (BRANCH: SKIP AHEAD WHEN LIMIT REACHED)
WHILE [#100 LT 10] DO1 (LOOP: SAME DO/END PAIRING AS FANUC)
#100 = #100 + 1
END1
N500 (CONTINUE)
Operator messaging works the Fanuc way and is worth using from your very first macro: #3000 = 100 (MESSAGE) stops the machine in alarm with your text on screen, and #3006 = 1 (MESSAGE) is a program stop with a message — an M00 that explains itself. Both show up in the worked example below as argument guards.
One Haas-specific habit worth forming early: G103 P1 limits block lookahead. The control normally reads far ahead of the executing block, which means a macro assignment can be evaluated before the motion block ahead of it has finished — poison for probing, where you read #5061+ after a skip move. Bracket lookahead-sensitive code with G103 P1 … G103.
Worked Example: G65 Bolt-Circle Macro
Haas gives you G70 for free, but a bolt-circle macro is still the right first project — it exercises argument decoding, validation, trig, and a WHILE loop in twenty lines, and the pattern generalizes to cycles G70 can’t do. The parent program:
O00100 (PARENT - DRILL A 6-HOLE CIRCLE)
T1 M06 (LOAD DRILL - NO G28 NEEDED FIRST)
G90 G54 G17 G00 X0 Y0 (ABSOLUTE, WORK OFFSET, XY PLANE)
G43 H01 Z1.0 S2500 M03 (LENGTH COMP, CLEARANCE, SPINDLE ON)
G65 P9010 A15. B2.0 H6. R0.1 Z-0.5 F8. (CALL THE MACRO)
(A=START ANGLE B=RADIUS H=HOLE COUNT)
(R=RETRACT PLANE Z=DEPTH F=FEED)
G00 Z1.0 M09 (RETRACT, COOLANT OFF)
G53 G00 Z0. M05 (MACHINE-COORD RETRACT, SPINDLE OFF)
M30
%
And the macro itself. Every G65 letter arrives in its fixed local variable — the map is the standard Fanuc one, and this example uses six of them:
| Argument | Lands in | Meaning here |
|---|---|---|
A15. | #1 | Angle of the first hole, degrees from 3 o’clock |
B2.0 | #2 | Bolt-circle radius |
F8. | #9 | Drilling feedrate |
H6. | #11 | Number of holes |
R0.1 | #18 | Canned-cycle R plane |
Z-0.5 | #26 | Hole depth |
O09010 (BOLT CIRCLE DRILL MACRO)
IF [#11 EQ #0] GOTO 900 (H MISSING? #0 IS NULL, NOT ZERO)
IF [#2 EQ #0] GOTO 901 (B RADIUS MISSING?)
IF [#26 EQ #0] GOTO 902 (Z DEPTH MISSING?)
#30 = 0 (HOLE COUNTER, START AT 0)
#31 = 360. / #11 (ANGLE BETWEEN HOLES)
WHILE [#30 LT #11] DO1 (ONCE PER HOLE)
#32 = #1 + #30 * #31 (THIS HOLE'S ANGLE = START + N*STEP)
#33 = #2 * COS[#32] (X = RADIUS * COS - DEGREES, LIKE FANUC)
#34 = #2 * SIN[#32] (Y = RADIUS * SIN)
G90 G00 X#33 Y#34 (RAPID OVER THE HOLE)
G99 G81 Z#26 R#18 F#9 (DRILL IT, RETURN TO R PLANE)
#30 = #30 + 1 (NEXT HOLE - FORGET THIS = DRILL FOREVER)
END1
G80 (CANCEL THE CYCLE BEFORE RETURNING)
M99 (BACK TO THE PARENT)
N900 #3000 = 100 (NO HOLE COUNT H) (ALARM WITH MESSAGE)
N901 #3000 = 101 (NO RADIUS B)
N902 #3000 = 102 (NO Z DEPTH)
The #0 checks at the top are the difference between a shop macro and a good one: a forgotten argument stops the machine with a readable alarm instead of drilling six holes at radius zero. Null (#0) and zero are different values — a null #11 means H was never passed at all. If you’d rather run this whole pattern from one file, the loop body could just as easily live at an N-number and be called with M97.
A Quick Taste of Probing
G31 works the Fanuc way: feed until the skip input fires, and the control latches the touch position in the skip variables — #5061/#5062/#5063 for X/Y/Z in work coordinates. Combine that with the writable work-offset variables (#5221–#5226 for G54, matching Fanuc numbering) and a macro can set its own offsets:
(TOUCH TOP OF PART, SET G54 Z - PROBE ALREADY ACTIVE IN SPINDLE)
G103 P1 (LIMIT LOOKAHEAD - READ SKIP VARS SAFELY)
G90 G00 Z1.0 (RAPID ABOVE EXPECTED SURFACE)
G31 Z-0.5 F10. (FEED UNTIL THE PROBE TOUCHES)
#5223 = #5223 + #5063 (SHIFT G54 Z BY THE TOUCH POSITION)
G00 Z1.0 (RETRACT)
G103 (RESTORE NORMAL LOOKAHEAD)
This is deliberately bare — no protected positioning, no double-touch, no probe on/off M codes, which a production routine needs. The full treatment, including the Renishaw/WIPS cycle library that ships on most Haas machines, is at Haas Renishaw Inspection.
DPRNT & Data Output
DPRNT works on Haas with the standard Fanuc syntax — literal text plus #-variables with width/precision format codes. On the Classic control the destination was the serial port; on the NGC, settings route DPRNT output to a file on USB or a Net Share (or over the network), which turns any macro into a no-hardware data logger: probe results, cycle counts, and in-process measurements land in a text file you can open at your desk.
(LOG A MEASURED DIAMETER AND A RUNNING PART COUNT)
#101 = 1.9998 (PRETEND THIS CAME FROM A PROBE ROUTINE)
#102 = #102 + 1 (RUNNING PART COUNTER)
DPRNT[PART*#102[40]*DIA*#101[24]]
(PRINTS: PART 12 DIA 1.9998 - [40] = 4 DIGITS, 0 DECIMALS)
( [24] = 2 DIGITS, 4 DECIMALS)
The asterisk prints as a space, and the bracketed digits after each variable set the integer/decimal field widths. Formatting rules, destination setup on both control generations, and collection patterns are covered in External Output (DPRNT).
Porting Checklist: Fanuc Program onto a Haas
When a known-good Fanuc program (or post) lands on a Haas, walk this list before cutting metal:
| Check | Why |
|---|---|
Replace G54.1 Pn with G154 Pn | Haas does not use G54.1; the direct equivalent is G154 with the same P number (P1–P99). |
| Confirm Setting 33 matches the program’s assumptions | Any G52 or G92 in the program means different things in FANUC vs HAAS vs YASNAC mode. |
| Confirm Setting 58 (cutter comp type) | Comp entry/exit moves differ between the FANUC and YASNAC behaviors — a program proven under one can gouge under the other. |
| Simplify the tool change | G91 G28 Z0 M06 runs but wastes motion; Tn M06 is the native idiom, with the next Tn staged early in the block after the change. |
Audit #500-range variables | If the machine has a probing package, calibration data occupies variables just above #549 — a Fanuc macro that scribbles there will quietly break probing. |
Add G103 P1 around skip/read sequences | Lookahead can evaluate macro reads before the motion they depend on has happened. |
| Re-point DPRNT | Output goes wherever the NGC settings route it — USB, Net Share, or network — not to a serial port that may not exist. |
None of these are exotic — they are the same seven items every Fanuc-to-Haas port trips over, and all of them are five-minute checks at the control.
▶ Open the Macro Playground — pick the Haas control and run these macros in your browser with live variable values.
Where to Go Next
This page is the Haas on-ramp; the deeper references are Haas System Variables for the full #-variable map, Haas Settings & Parameters for the settings that change program behavior (including the coordinate and cutter-comp compatibility switches above), and Haas Alarms & Diagnostics when the control talks back. New to macros entirely? Start at What Is a CNC Macro? — everything there applies to Haas directly.
References
- Haas Automation, Mill Operator’s Manual (Next Generation Control), Haas Automation Inc.
Have a question or want to contribute?
Contact us with corrections, additions, or topics you'd like covered.
Get in Touch