FANUC Custom Macro B Guide: Variables, IF/WHILE, and G65/G66 Macro Calls

27 August 2026

Mentor CNC Editorial Team

What is a custom macro, and how does it differ from a subprogram?

A subprogram (M98) repeats the same moves. A custom macro lets the program calculate, decide and loop. The difference in one line: a subprogram always makes the same part; a macro makes a different part depending on the dimensions you give it.

A concrete example: a bolt circle with 12 holes. With a subprogram you write 12 sets of coordinates. With a macro you supply the diameter and the number of holes, and the control works out the coordinates. When the diameter changes you change one number.

When is a macro worth writing?The same geometry recurs at different sizes · A cycle does not exist on your machine and you want to write it · A decision has to be made from a probe measurement · Values such as tool life or a part counter have to be read from the program.

Variables: four different kinds on a FANUC

A variable is written with #. The number you use determines where the variable is valid and whether it survives a power cycle. Not knowing that distinction is the most common mistake on the shop floor.

NumberTypeWhere is it valid?What happens at power off?
#0NullEverywhereAlways null, cannot be written, read only
#1 – #33Local variableOnly inside that macroCleared. Another macro using #1 cannot corrupt yours.
#100 – #199Common variable (volatile)Main program, subprograms and all macrosCleared — reset when power is removed
#500 – #999Common variable (retained)Main program, subprograms and all macrosRetained — the value survives a power cycle
#1000 and aboveSystem variableEverywhereThe control’s own data: offsets, position, alarms, clock, parameters
The distinction that mattersValues that must survive the end of a shift — a tool life counter, a part count — belong in #500–#999. Put them in #100–#199 and they reset when the machine is switched off, which you usually discover the next morning.

A note from the source manual: depending on bit 6 (NCV) of parameter 8135, the ranges #150–#199 and #550–#999 become usable. Variables in #500–#999 can also be made write protected (read only) by parameter.

Commonly used system variables

System variables give a macro access to the control’s own data. With them a macro can raise an alarm, measure time, read the current G code and axis position, and change work offsets. The numbers below follow the FANUC 0i-F manual (B-64604EN) for machining centers (M series). Some numbers differ on older series and on lathes; check the manual for your machine.

NumberRead / writeWhat it does
#3000WriteMacro alarm. #3000=1 (MESSAGE) stops the program with an alarm. With 6008#1 (MCA) = 0 the alarm screen shows MC3001 MESSAGE; the alarm number is 3000 plus the value written.
#3001Read / writeClock 1, in milliseconds. Starts from 0 at power-on and counts continuously.
#3002Read / writeClock 2, in hours. Counts only while the cycle start lamp (STL) is on; the value is kept at power-off.
#3006WriteStop with a message. #3006=1 (MESSAGE) runs the program up to the previous block, then stops and shows the message on the operator message screen.
#3901 / #3902Read / writeTotal number of machined parts / number of required parts.
#4001 – #4030ReadG code modal states of the block the control has most recently read. The last two digits are the G code group: #4003 is group 03 and returns 90 (G90) or 91 (G91).
#4201 – #4230ReadG code modal states of the block currently executing.
#4107, #4109, #4111, #4119, #4120ReadD, F, H, S and T values of the most recently read block.
#5001 – #5020ReadEnd point of the previous block (work coordinates, without offsets). Can be read during motion.
#5021 – #5040ReadCurrent position in machine coordinates.
#5041 – #5060ReadCurrent position in work coordinates.
#5061 – #5080ReadPosition where the G31 skip signal was received. The basis of probing macros.
#5081 – #5100ReadTool length offset of the executing block.
#5201 – #5220Read / writeExternal (EXT) work offset.
#5221 – #5240Read / writeG54 offset: #5221 axis 1, #5222 axis 2, #5223 axis 3. G55 starts at #5241; each system is 20 numbers further on.
#7001 onwardRead / writeG54.1 P1 and further additional work coordinate systems (option).
#2001 onwardRead / writeTool offsets. The numbering depends on the offset memory type and the number of offsets.

The axis number is the axis order on the machine; on most vertical machining centers 1 = X, 2 = Y, 3 = Z.

Example: save and restore a mode, check the part count

#1=#4003 ; 90 or 91: save the current mode G91 G01 Z-1. F100 ; work incrementally inside the macro … G#1 ; restore the previous mode (G90 or G91)#5223=#5223+0.05 ; raise the G54 Z offset by 0.05 mmIF[#3901 LT #3902] GOTO 100 #3006=1 (PART COUNT REACHED) ; stop with a message N100 M99
Caution#5021 and #5041 may not read accurately while an axis is moving; the manual does not guarantee values read during motion. Make sure motion has finished before reading them. The #4001 series reflects the block the control has read ahead; use the #4201 series when you need the mode of the block that is actually executing. Writing to offset variables changes the real work zero and tool offsets; try a new macro first without a part and in single block mode.

Using a variable in place of an address

The value following an address can be replaced by a variable:

#33=1.5 F#33 ; the same as F1.5#18=20.0 Z-#18 ; the same as Z-20.0#130=3.0 G#130 ; the same as G3

The rules, straight from the source

  • Addresses O and N cannot take a variable. O#27 or N#1 is not allowed.
  • The n in an optional block skip /n cannot be a variable.
  • A variable number cannot be specified by a direct variable. ##30 is wrong; write #[#30].
  • No value exceeding the maximum allowable for an address can be specified. With #140=10000, G#140 is out of range.
  • Used as address data, a variable is rounded automatically. On an IS-B machine (1/1000 mm), #1=12.3456 makes G00 X#1 become G00 X12.346.
  • The decimal point can be omitted: #1=123 gives a value of 123.000.
  • Expressions are allowed: X[#24+#18*COS[#1]] or Z-[#18+#26].

The null variable trap

This causes more trouble than anything else when writing macros. A variable whose value has not been defined is called null. #0 and #3100 are always null.

1. When a null variable is quoted, the address itself is ignored

WrittenExecuted when #1 is nullExecuted when #1 = 0
G90 X100 Y#1G90 X100 — no Y at allG90 X100 Y0

A null variable is not the same as zero. The Y axis does not move, and no alarm is raised — it is simply skipped. This is exactly what happens when you forget to pass an argument to a macro.

2. Behavior in assignment and arithmetic

ExpressionResult when #1 is nullResult when #1 = 0
#2 = #1 (local)null0
#2 = #1 * 500
#2 = #1 + #100
#2001 = #1 (system variable)00

Assign a null value directly and it stays null; put it through any calculation and it becomes 0. Assigned to a system variable it always becomes 0.

3. Null in comparisons

The manual’s own wording: null differs from 0 only for EQ and NE. For GE, GT, LE and LT, null is treated as equal to 0.

; with #1 null: #1 EQ #0 → true #1 NE 0 → true (note: null is not considered equal to 0) #1 GE #0 → true #1 GT 0 → false #1 LE #0 → true #1 LT 0 → false
What this means in practiceTo test whether an argument was supplied, write IF [#1 EQ #0] GOTO 99 — not IF [#1 EQ 0]. The second one also fires when the user really did enter 0.

Arithmetic operations and functions

OperationSyntaxNotes
Add / subtract#i = #j + #k · #i = #j - #k 
Multiply / divide#i = #j * #k · #i = #j / #kDivision by zero raises PS0112
Sine / cosine / tangentSIN[] · COS[] · TAN[]Angles are in degrees, not radians
Arc tangentATAN[]/[]Angle from the ratio of two values
Square rootSQRT[] 
Absolute valueABS[] 
RoundingROUND[]To the nearest integer
Truncate / round upFIX[] · FUP[]Discards the fraction / raises to the next integer
Natural log / exponentialLN[] · EXP[] 
Read a parameter#i = PRM[#j]Reads a machine parameter from the program
The most common trigonometry mistakeAngles in FANUC macros are in degrees. SIN[30] returns 0.5. Do not convert to radians.

Comparison and logic operators

FANUC macros do not use >, < or =. Two-letter abbreviations are used instead:

OperatorMeaningMaths
EQEqual to=
NENot equal to≠
GTGreater than>
GEGreater than or equal to≥
LTLess than<
LELess than or equal to≤

Conditions can be combined with AND and OR:

IF[[#1 EQ #2] AND [#3 EQ #4]] THEN#5=0 ; IF[[#1 EQ #2] OR [#3 EQ #4]] THEN#5=0 ;

Branching and looping: GOTO, IF, WHILE

Unconditional branch — GOTO

GOTO 1 ; ; branch to N1

Conditional branch — IF … GOTO

IF [#1 GT 10] GOTO 2 ; ; if #1 > 10, branch to N2

Conditional assignment — IF … THEN

IF [#1 EQ #2] THEN#3=0 ; ; if #1 equals #2, set #3 to 0

Looping — WHILE … DO … END

WHILE [condition] DO m ; ; m = 1, 2 or 3 ; processing END m ;

While the condition holds, everything between DO and END repeats. m can only be 1, 2 or 3.

The nesting rule — important

Nested loops may reuse the same number, but the loops must nest completely inside one another. Overlapped DO ranges raise alarm PS0124.

; CORRECT — fully nested WHILE […] DO 2 ; WHILE […] DO 1 ; END 1 ; END 2 ;; CORRECT — sequential, the same number can be reused WHILE […] DO 1 ; END 1 ; WHILE […] DO 1 ; END 1 ;; WRONG — overlapped ranges, PS0124 WHILE […] DO 1 ; WHILE […] DO 2 ; END 1 ; END 2 ;

Calling a macro: G65, G66 and G67

G65 — simple call

Calls the macro once and passes the arguments.

G65 P9100 X100.0 Y50.0 R30.0 Z-50.0 F500 ; ; P : number of the program to call ; X, Y, R, Z, F : arguments passed to the macro
RuleG65 must be specified before any argument. It cannot sit in the middle of the line.

Argument table (argument specification I)

AddressVariableAddressVariableAddressVariable
A#1I#4T#20
B#2J#5U#21
C#3K#6V#22
D#7M#13W#23
E#8Q#17X#24
F#9R#18Y#25
H#11S#19Z#26

So in the example above, inside the macro #24 = 100.0, #25 = 50.0, #18 = 30.0, #26 = −50.0 and #9 = 500.

Argument specification II

A, B and C are used once each; I, J and K can be used up to ten times each. This is the method for passing many points, for example a free-form contour. If both methods appear in one block, the type specified last takes effect.

G66 / G67 — modal call

After G66, the macro is called after every block that moves an axis, until G67 cancels it.

G66 P9100 L2 A1.0 B2.0 ; G00 X10. Y10. ; ; the macro runs after the move G00 X20. Y20. ; ; and again G67 ; ; cancel the modal call ; P : number of the program to call ; L : number of repetitions

This is very useful for hole patterns: define the drilling macro once with G66, then just write the coordinates.

Nesting limits

  • Macro calls (including G65 and G66/G66.1) can nest up to five levels.
  • Subprogram calls, macro calls included, can nest up to 15 levels.
  • Each macro call raises the local variable level by one — the inner macro’s #1 is not the outer one’s.

The mistakes people actually make

Writing persistent data into the #100 series

A part counter or tool life value in #100–#199 resets when the machine is switched off. Anything that must persist belongs in #500–#999.

Treating a null variable as zero

When an argument is not supplied the variable stays null, not zero. If #1 is null in a line reading Y#1, the Y axis does not move at all — and no alarm appears.

Writing ##30

To give a variable number with a variable, write #[#30]. ##30 is invalid.

Using a single equals sign in a comparison

IF [#1 = 10] is not valid. It must be IF [#1 EQ 10].

Overlapping WHILE loops

DO and END ranges must nest completely. A partially overlapping range raises PS0124.

Putting an NC statement and a macro statement in one block

A motion command and a macro expression on the same line raises PS0127. Put the macro expression on its own line.

Using radians in trigonometry

Angles in FANUC macros are in degrees. SIN[30] = 0.5.

Nesting macro calls deeper than five levels

The macro nesting limit is five. Beyond that you get PS0122.

Writing G65 in the middle of a line

G65 must come before all arguments.

Frequently asked questions

Does every FANUC machine have custom macro?

No, it is an option. There are two levels, Custom Macro A and Custom Macro B; B is the common one today. The quickest way to find out is to type #100=1 in MDI and run it — if the option is absent you get an alarm.

What is the practical difference between #100 and #500?

#100–#199 is for temporary arithmetic and clears at power off. #500–#999 is retained and survives a power cycle. Anything that has to carry across shifts — part counts, tool life — always goes in the #500 series.

Can I change tool offsets from the program?

Yes. Tool offsets are read and written through system variables. The numbering varies with the control series and the number of offsets, so check the system variable table in your machine’s manual. Writing a probe measurement result straight into an offset works exactly this way.

Should I use a subprogram or a macro?

If the same moves repeat identically, a subprogram (M98) is enough and simpler. If coordinates have to be calculated as the size changes, or a decision has to be made, you need a macro.

Why can I only use 1, 2 and 3 in a WHILE loop?

The DO number on a FANUC is limited to 1–3, so you can nest at most three levels of loop. In sequential (non-nested) loops the same number can be reused.

How do I protect a macro program?

Macro programs are usually stored under 9000-series program numbers (O9000–O9999), and those programs can be protected from display and editing by parameter. Common variables #500–#999 can also be made read only by parameter.

What is the equivalent of Siemens R parameters?

Roughly the FANUC #100 and #500 series common variables. Siemens additionally allows named variables via DEF; on a FANUC variables have no names, only numbers.

What do I see on screen while a macro runs?

Macro statements produce no motion, so the block display may not appear to advance. Use the control’s macro variable screen to watch the values — it is the most practical diagnostic during commissioning.

Safety warningBecause a macro calculates its motion, a wrong variable can send the tool somewhere unexpected. Every newly written macro should be checked first with graphics or dry run, then tried with no part clamped, in single block with a low feedrate override. WHILE structures that can loop forever and macros that write to system variables deserve particular supervision on their first run.
Mentor CNC note: The variable ranges, syntax and limits on this page are based on Chapter 16 of FANUC’s official Series 0i-MODEL F Operator’s Manual (B-64604EN/01). Custom macro is an optional function; whether your machine has it, which variable ranges are usable and which system variable numbers apply all depend on the control series, the options purchased and the parameter settings. The machine tool builder’s documentation always takes precedence. This page is for training purposes and does not replace the official manual.

Sources

  • FANUC — Series 0i-MODEL F, Common to Lathe System / Machining Center System, OPERATOR’S MANUAL, document no. B-64604EN/01: Section 16.1 Variables, 16.2 System variables, 16.5 Arithmetic and logic operation, 16.7 Reading parameters, 16.8 Macro statements and NC statements, 16.9 Branch and repetition (IF, GOTO, WHILE), 16.10 Macro call (G65, G66, G67), 16.13 Registering custom macro programs, 16.16 Restrictions
  • FANUC — Series 0i-MODEL F MAINTENANCE MANUAL, B-64605EN/01, Appendix A (for the macro-related PS alarms)