Fanuc Macro Executor & P-Code Macros
What Is the Macro Executor?
Every Fanuc-controlled machine ships with macros you will never see: the drilling packages, probing suites, tool changers, and custom screens the machine tool builder wrote. Most of them do not live in program memory at all. They were written as ordinary custom macros, then compiled into an execution format called P-code and registered into a ROM module (Flash ROM) at the builder's factory. The Macro Executor is the CNC function that runs those compiled programs.
Fanuc's Macro Compiler/Executor manual (B-61803E-1) lists why builders do this: the compiled macro executes faster than interpreted code; ROM registration eliminates battery-backup risk and prevents damage through mis-operation; the registered program is not shown on the program display, protecting the builder’s know-how; and it frees the program edit memory for your parts. The end user calls the macros with ordinary G, M, or T codes without ever being conscious of the registered program.
The manual defines the vocabulary this article uses: a P-CODE program is an execution-type macro prepared by the machine tool builder, compiled and registered to ROM. Within it live execution macros (programs that operate the machine), conversational macros (programs that operate the screen), and auxiliary macros (background helpers for the other two). Your programs in edit memory are user programs.
Interpreted vs Compiled Macros
Everything else in this wiki's macro section — parametric programming, custom cycles — covers interpreted Custom Macro B: source text in program memory, read block by block at runtime. P-code is the same language after a compile step.
| Aspect | Custom Macro B (interpreted) | P-Code Macro (compiled) |
|---|---|---|
| Lives in | Part program edit memory (battery-backed) | ROM / Flash ROM module (F-ROM on 15-B and 30i) |
| Speed | Interpreted block by block | Pre-converted to execution format — higher execution speed |
| Visibility | Listed and editable on the program screen | Not indicated on the program display — builder know-how protected |
| Who writes it | You (the shop) | The machine tool builder |
| Call aliasing set by | CNC parameters (6050-6059 etc.), changeable at the control | Compile parameters (9010-9047), burned in at compile time |
| Capacity | Your tape-memory option | 16/18: max 400 programs per ROM; 30i-A: max 1000 P-code macros, file up to 4096 KB |
| Screen programming | Not possible | Conversational macros draw custom screens |
The Compiler Toolchain
The toolchain is a PC (historically the FANUC SYSTEM P), not the control. On the 0i/16/18/20/21 generation the builder writes normal macro source, compiles it with the FAPT Macro Compiler (manual B-66102E for the PC version), sets the compile parameters, and writes the result to a ROM module — offered in 128 KB to 1 MB sizes — via an FA writer. A P-code loader lets the builder load into a RAM module over RS-232C to confirm operation before committing to ROM.
On the Series 30i-A the flow is modern: the compiler links the macro with its compile parameters into a MEM-format file, which is loaded from a memory card into F-ROM (compiler manual B-66264EN). The P-code file size is selected by compile parameters 9000#2–9001#2 in steps from 256 KB to 4096 KB, and the manual warns that a loaded file larger than the purchased option keeps the CNC from starting with USER FILE(P-CODE):SIZE OVER — the executor is an option, not a birthright.
The O9000 Neighborhood
Whether or not your machine has a compiled executor, builders also park interpreted macros in the protected program-number ranges. The parameter manual (B-63530EN/03 for 16i/18i-B) documents the tiers:
| Parameter | Protects | Effect when set |
|---|---|---|
3202#0 (NE8) | O8000–O8999 | Inhibits deletion, output, number search, editing, registration, collation, and display |
3202#4 (NE9) | O9000–O9999 | Same seven operations inhibited — the programs effectively vanish from the control |
3202#7 (PSR) | — | 1 = allow number search (and display) of a protected program |
3210 / 3211 | Password / Keyword | If 3210 ≠ 0 and ≠ the keyword in 3211, NE9 is forced to 1 and cannot be cleared. The password value is never displayed; a G10 write attempt raises P/S alarm 231. The keyword (3211) clears to 0 at power-off |
3204#3 (P8E) / 3204#4 (P9E) | O80000000–O89999999 / O90000000–O99999999 | Same protection for the 8-digit program-number option; 3204#5 (SPR) relocates the 9000 range to 90009000+ |
3220–3223 | Password-locked range | 3220/3221 password/keyword lock display, input/output, and editing of the program range set in 3222 (min) and 3223 (max); with both set to 0 the range defaults to O9000–O9999 |
So when you "can't find" O9010 on a machine: it is either interpreted but hidden behind NE9/password, or it is P-code and was never in program memory to begin with.
Call aliasing. For interpreted macros, CNC parameters map custom codes to the protected programs (parameters 6050–6059 map G codes to O9010–O9019 — covered in Custom G/M Cycles). The executor has a parallel scheme, except the mapping is a compile parameter fixed inside the ROM (B-61803E-1, Table 3.1):
| Calling code | Call type | Programs called | Set by |
|---|---|---|---|
| G code | Macro call / modal call | O9010–O9019 | Compile params 9013–9022 (negative value = modal; canceled by G167 or the code in 9034) |
| M code | Macro call | O9020–O9029 | Compile params 9023–9032 |
| M code | Subprogram call | O9001–O9003 | Compile params 9010–9012 |
| M code range | Subprogram call | O9009 | Compile params 9042 (low) / 9043 (high) |
| T code | Subprogram call, T value → #149 | O9000 | Compile param 9002#0 (TCAL) |
| T code | Macro call, T → #27, active G codes → #28–#32 | O9008 | Compile param 9005#7 (TMACC) |
| Specified address | Subprogram call, value → #146/#147 | O9004 / O9005 | Compile params 9002#1/#2 (ACL1/ACL2) + ASCII code in CNC params 6090/6091 |
| G code range | Macro call | Builder-chosen block of programs | Compile params 9045 (first G) / 9046 (count) / 9047 (first program) |
| Axis address | Macro call, axis value → #27 | O9009 or O9031–O9038 per axis | Compile params 9005#0–#3, 9008#0–#3, 9005#4 (AXCLS) |
(BUILDER SET COMPILE PARAM 9013 = 100 AT COMPILE TIME)
O0001
G100 X50.0 Y25.0 Z-10.0 F500.0 (CALLS COMPILED O9010 - INVISIBLE TO YOU)
(X50. ARRIVES IN #24, Y25. IN #25, Z-10. IN #26, F500. IN #9)
...
(9013 = -100 WOULD MAKE G100 A MODAL CALL:)
(EVERY MOTION BLOCK RECALLS O9010 UNTIL G167 CANCELS)
M30
This is why the same drill cycle G code behaves differently across two builders using the same Fanuc control: each burned their own mapping. Argument passing itself is identical to G65 — the standard letter-to-variable table applies (see Custom G/M Cycles).
Execution Macros vs Conversational Macros
The executor runs two very different worlds side by side.
Execution macros run inside automatic operation, called from the user program by the aliased codes above. They are written like any custom macro (G65 calls, IF/GOTO, WHILE/DO) with executor-specific limits: on the 16/18 generation, macro and subprogram nesting inside the P-code world is 4 levels each — separate from the user program's nesting — and modal calls cannot be issued from within an execution macro.
Conversational macros are the builder's custom screens. Pressing the CUSTOM function key starts the conversational macro whose program number is held in execution control variable #8500 (initial value set by compile parameter 9038; CUSTOM screens 2 and 3 use #8550 and #8551, initialized by parameters 9040/9041). Writing a new program number into #8500 and executing M99 erases the screen and transfers control to the new screen program. The manual is explicit about the programming model: the screen program must cycle and "execute M99 without fail" — "program the conversational program so that it will be a cyclic like the PMC ladder program." Screen content is drawn with dedicated control codes: G202 (screen clear), G240 (color), G242 (drawing start point), G243 (character display), G01/G02/G03 (line and arc drawing), with key input read through control variable #8501 and data input through #8502.
A minimal screen program, using only mechanisms the manual documents, looks nothing like a machining macro:
(O6000 - CONVERSATIONAL MACRO MAIN, #8500=6000 SET BY COMPILE PARAM 9038)
(RUNS ONLY WHILE THE CUSTOM SCREEN IS DISPLAYED)
G202 X1 (CLEAR THE SCREEN)
G243 X10 Y2 (DRILL PACKAGE V1) (DRAW A TITLE STRING)
G243 X10 Y5 (SET DEPTH THEN PRESS INPUT)
IF [#8501 EQ 0] GOTO 99 (NO KEY PRESSED THIS PASS - #8501 IS KEY INPUT)
#10100 = #8503 (STORE KEYED-IN VALUE IN A P-CODE VARIABLE)
N99 M99 (ALWAYS END THE PASS - CYCLIC, LIKE PMC LADDER)
The value survives in #10100 after power-off; the next time the operator runs the aliased cycle, the execution macro reads the same P-code variable. That pair — screen program writing, execution macro reading, nonvolatile P-code variables in between — is the skeleton of every builder drilling package and conversational front-end.
Auxiliary macros are the third citizen: they execute always, regardless of which screen is displayed, running the program number fixed by compile parameter 9039. They may not use the screen-display codes or the key/cursor variables #8501–#8509 — they are pure background logic.
Two scheduling facts explain behavior you may have noticed on real machines. Conversational and auxiliary macros run at a lower internal priority than CNC operation, so a busy program never slows down because of a fancy screen — but the screen may update slowly while cutting. And because execution macros preempt the background macros, the manual warns against sharing a common variable as a read-modify-write flag between the two worlds: an execution macro's write can be silently overwritten when the interrupted auxiliary macro completes its own write.
Builders are not entirely without tools on the control itself: the 30i executor manual devotes a chapter to a debugging function — a debugger screen for displaying and setting execution state, plus direct setting by parameter and key — the modern replacement for the older generations’ load-into-RAM-and-try workflow.
P-CODE Variables
The executor adds whole variable ranges that interpreted Macro B does not have — and walls off some that it does. Crucially, on the 16/18/0i generation the common variables seen by P-code programs (#100–#149, #500–#531) are completely different variables from the #100s and #500s in your user program. The builder's compiled world and your world do not collide.
| Range | Type | 0i/16/18/20/21 (B-61803E-1) | 30i-A (B-63943EN-2) |
|---|---|---|---|
#1–#33 | Local | Per call level; #1–#99 double as array-reference variables in screen macros | Same; conversational/auxiliary locals are independent of execution-macro locals |
#100–..., #500–... | Common | #100–#149 (volatile) and #500–#531 (retained), separate from user-program commons | #100–#199 / #500–#999; bits MV0–MV7 of parameter 9034 choose per-block whether they are shared with custom-macro commons or kept as an independent P-code set |
#10000– | P-CODE variables | Allocated in blocks of 100 by compile parameter 9037 (e.g. 9037=2 → #10000–#10199); consume part-program memory; retain values through power-off | #10000–#19999; count set by parameter 9053 (max 10000); float or integer per 9033#3; retained through power-off |
#20000– | Extended P-CODE variables | Count set by parameter 9044; float (12 per set) or integer (30 per set, −32768 to 32767) chosen by 9002#3 (EVF); retained | #20000–#89999; count set by parameter 9054 (max 70000); float/integer per 9033#4; retained |
#8500– | Executor control | #8500/#8550/#8551 screen selection, #8501 key input, #8502 data input, and the rest of the #85xx control set | Same concept, extended (#8530, #8552, etc.) |
#99100–#99999 | Custom-macro window | — | Reach the user's commons from P-code: variable number + 99000 (#99100↔#100 ... #99999↔#999) |
#101000–#199999 | System-variable window | — | System variable number + 100000, because #10000+ is occupied by P-code variables (#1000–#9999 remain directly usable) |
The persistence is the point: P-code and extended P-code variables are the builder's nonvolatile database — probe calibration values, cycle defaults, tool-magazine maps — surviving power cycles without touching your #500s. On the multi-path 30i, parameters 9051/9052 even let paths share one P-code variable area, and a screen or execution macro can read and write another path's variables with G316. You can inspect (and, where allowed, set) them from the control: the P-CODE VARIABLE screen appears under the OFFSET/SETTING key when the builder enables it (executor parameter 9000#1, NDP).
What This Means When You Buy a Machine
- The builder's cycles are not editable — P-code lives in ROM/F-ROM and never appears on the program screen. Interpreted builder macros in O8000/O9000 are hidden by NE8/NE9 and possibly a password (3210). Do not fight this; it also protects you from corrupting the tool changer.
- Keep your own macros out of the builder's ranges. If NE9 hides O9000–O9999 you cannot even see your own program there. Use the unprotected user range and, for your custom G codes, pick numbers the builder has not aliased — a compiled G-code mapping (compile params 9013–9022) silently wins over your intent and cannot be changed at the control.
- Watch the shared resources. On 30i-class controls the builder chooses (parameter 9034) whether P-code commons overlap your #100–#199/#500–#999; on older controls the worlds are separate but builder macros may still read/write your commons through the documented windows. Ask which #500-range variables the builder reserves.
- Ask the builder for: the list of aliased G/M/T codes and their argument letters, the reserved common-variable and P-code-variable assignments (probe results, cycle settings), whether a 9000-range password is set, and the macro executor option state if you plan to buy third-party probing software — vendors like Renishaw ship their interpreted suites into the same O9000 neighborhood.
- Before any memory clear or control swap, remember P-code variables hold calibration data. They survive power-off, but a RAM initialization at the loading screen explicitly clears the 10000-level variables (B-61803E-1).
Generations
| Control family | Compiler/Executor manual | Generation notes (as documented) |
|---|---|---|
| Series 0 (0-C era) | B-61393E-1 | Source edited on the SYSTEM P-G, burned to ROM module with a PMC writer; conversational AUX/MENU/MACRO screens |
| Series 15-B | B-62073E-2 | P-code registered into F-ROM via ROM cassette; compiled on System P (B-66032E) or PC (B-66102E); 15-B can also compile on-control |
| 0i/16/18/20/21 (incl. i-A/i-B) | B-61803E-1 | FAPT Macro Compiler on PC; ROM modules 128 KB–1 MB; RS-232C P-code loader for RAM testing; max 400 programs; #20000+ extended variables arrive (i-series lifts the 160 m tape-memory floor) |
| 30i/31i/32i-A | B-63943EN-2 | Compile + link to MEM-format file, loaded by memory card into F-ROM; P-code file to 4096 KB, 1000 macros, 8-digit program numbers; multi-path variable sharing (9048–9054, G316); 10-level IF nesting, 2000 IF statements |
The arc across the manuals is steady: storage grows from kilobyte-scale ROM modules burned on an FA writer to megabyte-scale F-ROM loaded from a memory card, program counts go from 400 to 1000, and the screen side keeps pace with the display hardware — the 0i/16/18/20/21 manual carries appendices for VGA graphics display units and background-color custom screens, while the 30i manual documents richer drawing (graphic filling, rectangular display, marking) in its executor function chapter. The language itself barely changes: source written for one generation's compiler is recognizably the same Custom Macro B throughout.
▶ Open the Macro Playground — interpreted Custom Macro B, the language P-code macros are compiled from, runs right in your browser with live variable values. Write the macro here; the executor is just its compiled cousin.
See also: Parametric Programming for designing macro families, Custom G/M Cycles for the interpreted G/M aliasing parameters, Macro Structure for G65/G66 call mechanics, and Fanuc Series Guide for which control family your machine belongs to.
References
- Fanuc, Series 0i/16/18/20/21 Macro Compiler/Macro Executor Programming Manual, B-61803E-1/10.
- Fanuc, Series 30i/300i/300is-A Macro Compiler/Macro Executor Programming Manual, B-63943EN-2/01.
- Fanuc, Series 16i/18i-B Parameter Manual, B-63530EN/03 (program protection parameters 3202–3223).
- Fanuc, Series 0 Macro Compiler/Macro Executor Programming Manual, B-61393E-1/02.
- Fanuc, Series 15-B Macro Compiler/Macro Executor Programming Manual, B-62073E-2/03.
Have a question or want to contribute?
Contact us with corrections, additions, or topics you'd like covered.
Get in Touch