Repeated hole patterns rarely require a long NC program. If the coordinates form a simple row, a G81 canned cycle, G91 incremental positioning, and an L repeat count may be all that is needed. A rectangular grid, a family of part sizes, or a pattern whose pitch changes frequently creates a different problem: editing every coordinate by hand becomes slow and error-prone. Macro programming keeps the repeated logic in one subprogram and passes the dimensions and hole count as arguments.
The example below targets a Haas milling control with the macro option. Variable assignments, loop syntax, and subprogram calls differ between CNC controls and software configurations. Always check the manual for the actual control, verify the program graphically, and make the first run above the workpiece.
When a canned cycle is enough
G81 already removes most of the repeated code from a drilling operation. Once the cycle is active, the control drills at each new X/Y position until G80 cancels it. Haas documentation shows how an incremental X move and an L count can produce a row of equally spaced holes:
G90 G54 X10. Y20.
G43 H01 Z50.
G99 G81 Z-12. R2. F120.
G91 X25. L4
G80
G90
This is a good solution for one straight row. The first hole is made where G81 is activated, while G91 X25. L4 adds four more positions. Losing track of that first cycle execution can create an extra hole or shift the intended pattern.
Other canned cycles cover different drilling operations. G82 adds a dwell at the bottom, G83 is used for peck drilling, and G98 or G99 controls the return level. The selected cycle must suit the tool, depth, material, and chip evacuation conditions. A macro manages coordinates and repetition; it does not replace the correct drilling process.
What a parameterized macro adds
A rectangular grid can be defined with nine values: the first-hole coordinates, pitch along both axes, number of columns and rows, drilling depth, R plane, and feed rate. The operator changes one call, while nested loops calculate every hole position.
In the following call, X and Y define the grid origin, I and J define the pitch, and A and B define the number of columns and rows:
G65 P9010 X10. Y20. I25. J20. A4. B3. Z-12. R2. F120.
On a Haas control, G65 arguments are mapped to local variables in the macro subprogram. In this example, X maps to #24, Y to #25, I to #4, J to #5, A to #1, B to #2, Z to #26, R to #18, and F to #9. Each G65 call provides a new set of local variables, so the same macro can run with different dimensions and process values.
Macro for a rectangular grid
O09010 (DRILL RECTANGULAR PATTERN)
#30 = 0 (ROW INDEX)
WHILE [#30 LT #2] DO1
#31 = 0 (COLUMN INDEX)
WHILE [#31 LT #1] DO2
#32 = [#24 + #31 * #4] (CURRENT X)
#33 = [#25 + #30 * #5] (CURRENT Y)
G90 G00 X#32 Y#33
G81 Z#26 R#18 F#9
G80
#31 = #31 + 1
END2
#30 = #30 + 1
END1
M99
The outer loop counts rows and the inner loop counts columns. Each coordinate is calculated by adding the position index multiplied by the pitch to the starting coordinate. With A4 and B3, the macro drills twelve holes: four holes in each of three rows.
In this instructional version, G81 is activated and canceled at every position. That adds a small amount of execution overhead, but it keeps the canned-cycle state limited to one operation. A production macro can keep the cycle active across positions, provided that the programmer carefully controls the initial execution, modal state, and final G80 cancellation.
Main program and safe states
The macro should not infer the tool number, length offset, spindle speed, or work offset. Keeping those values in the main program makes the operation setup easier to review.
O01000
G90 G17 G40 G49 G80 G94
G54
T1 M06
S1800 M03
G00 X10. Y20.
G43 H01 Z50.
M08
G65 P9010 X10. Y20. I25. J20. A4. B3. Z-12. R2. F120.
G80
G00 Z50.
M09
M05
G53 G00 Z0.
M30
This program establishes the G17 plane, G90 absolute positioning, G54 work coordinate system, and G43 H01 tool length compensation before calling the macro. Safe heights, the machine-coordinate retract, and the equipment startup sequence must follow the requirements of the machine and the shop's approved program template.
Checks that belong in a production macro
Parameterized code reduces manual editing, but one incorrect argument can alter the entire hole pattern. A production macro should validate its inputs. Row and column counts must be positive integers, the R plane must be on the safe side of the workpiece surface, and the calculated X/Y positions must remain within the part boundary and machine travel.
- Confirm the argument-to-variable mapping for the actual control.
- Make sure each DO number has the correct matching END in nested loops.
- Calculate the final point in advance: X + (A − 1) × I and Y + (B − 1) × J.
- Check the sign of Z, the R-plane position, and the G98 or G99 return mode.
- Run graphics first, followed by single block with rapid override reduced.
- Where vises or clamps are present, check transfer moves as well as drilling positions.
Missing-argument protection is also useful. On controls where an omitted local variable has a null value, the macro can raise an alarm before any motion begins. The correct null-variable test and alarm syntax must come from the manual for that CNC control.
Where the macro saves work
For a one-off part with five known holes, a plain G81 cycle followed by a coordinate list is often easier to read. A macro becomes useful when the pattern changes according to a consistent rule: rectangular or circular arrays, several sizes of the same part, repeated holes under different work offsets, or a product family that shares one drilling process.
A maintainable macro has one well-defined job and a clear interface. Document every argument in program comments or in the setup sheet, and keep a valid sample call with the program. The setup person can then change the pattern dimensions and machining values without editing loop counters or coordinate formulas.
Haas provides official reference material for G65 argument mapping and local variables and for drilling canned cycles. Use those references as a starting point and confirm the behavior against the software version installed on the machine.