Supported RPG Features

Opcode Description
EVAL Assignment (extended factor 2)
EVAL-CORR Assign corresponding subfields between two data structures (extended factor 2; the (H) extender is free-format only)
IF / ELSEIF / ELSE / ENDIF Conditional logic
DOW / ENDDO Do-while loop
DOU / ENDDO Do-until loop
DO / ENDDO Counted loop (fixed-format only). Factor 1 = start (default 1), Factor 2 = limit (default 1), Result = index variable (optional). ENDDO Factor 2 = increment (default 1).
FOR / ENDFOR Counted loop with optional BY step and TO/DOWNTO direction. The loop index must be a numeric field with zero decimal positions - integer, unsigned, or zero-scale packed/zoned (a float or fractional fixed-point index is rejected; see TRN3017). The index value after the loop ends is one step past the limit, matching the IBM i increment-then-test model.
END RPG II/III block terminator. Closes whichever block is innermost - DO, DOW, DOU, IF, SELECT, or CAS - exactly as that block's RPG IV terminator (ENDDO / ENDIF / ENDSL / ENDCS) does. The two spellings may be mixed freely in one program, which is what partially modernised source looks like. FOR is RPG IV only and takes ENDFOR.
LEAVE Exit the innermost DO / DOW / DOU / FOR loop. Takes no operands; a conditioning indicator in fixed-format columns 9-11 applies as usual.
ITER Start the next iteration of the innermost loop. Takes no operands; a conditioning indicator applies as usual.
LEAVESR Return from the current subroutine. Takes no operands; a conditioning indicator applies as usual.
SELECT / WHEN / OTHER / ENDSL Case selection. Also supports the SELECT-with-operand form in both fixed- and free-format: SELECT operand; WHEN-IS value; WHEN-IN collection; ... OTHER; ... ENDSL;. WHEN-IS compares the operand for equality against the value; WHEN-IN tests membership, and accepts %LIST(...), %RANGE(...), or an array. In a fixed-format C-spec the operand and the WHEN-IS/WHEN-IN value both go in the extended factor 2 area. The two SELECT forms cannot be mixed: a SELECT that states an operand admits only WHEN-IS/WHEN-IN branches (TRN1049), and WHEN-IS/WHEN-IN require a SELECT that states one (TRN1050).
MONITOR / ON-EXCP / ON-ERROR / ENDMON Structured exception handling. ON-EXCP (free-format only) catches by message ID; ON-ERROR catches by status code.
DSPLY Display a message (full form: DSPLY source queue response - parameters are space-separated; optional output-queue is accepted but ignored on the JVM target). The optional response field receives the operator's reply: a character field is padded/truncated to its declared length; a non-float numeric field (packed, zoned, integer, unsigned) receives the reply as raw keyed digits, right-adjusted into the field's digit positions with the decimal point implied by its scale - matching MOVE character-to-numeric (a reply of 12 into a PACKED(5:2) field stores 0.12; 12345 stores 123.45; 42 into a PACKED(5:0) stores 42); a float field (FLOAT(4) or FLOAT(8)) instead value-parses the reply as a floating-point value in its external display representation, so a decimal point and E-notation are honored (a reply of 12.5 stores 12.5; 1.5E3 stores 1500). source (factor 1) may be omitted, leaving an inquiry that only reads a reply. In fixed-format, factor 1 holds a single operand - a field, a data-structure subfield, a named constant, a literal, an array element, an indicator, or a reserved word such as *DATE. A built-in function, an operator expression, or a figurative constant there is rejected with TRN1543, which displays a blank message. The free-format form takes an expression and is unaffected: DSPLY ('n=' + %CHAR(n));. The inquiry prompt shows the response field's current value so the operator can see it before replying: source, then four blanks (only when source is present), then the field's image - a non-float numeric as its unscaled digits right-justified in its declared digit count (PACKED(5:2) holding 1.25 shows ··125), a character field as its character image. Trailing blanks are not shown.
SND-MSG Send a message to the job log (free-format only). Syntax: SND-MSG [*type] message; where *type is *INFO (default), *ESCAPE, *COMP, *DIAG, *STATUS, or *NOTIFY. On JVM, *ESCAPE throws an exception (message ID CPF9898) that propagates to the caller and can be caught by MONITOR / ON-EXCP or MONITOR / ON-ERROR - an unmonitored *ESCAPE halts the program. Every other type (*INFO, *COMP, *DIAG, *STATUS, and *NOTIFY) posts to the job log (written to stderr) and the program continues to the next statement; *NOTIFY requests a reply but, with no reply source in a headless job, the default reply applies and execution continues. Every message, including *ESCAPE, is also retained on the program's message queue, where a message subfile can display it (see Message Subfiles). Supports %MSG(msgId:msgFile{:replacementText}) to construct a message from a message ID (on JVM, formatted as "msgId: replacementText" since message files are not available) and %TARGET(*SELF) / %TARGET(*CALLER) to direct the message to a call-stack entry (accepted syntactically; no-op on JVM since call-stack targeting is an OS facility). A fixed-format program reaches the same queue by calling QMHSNDPM - see Message Subfiles.
FOR-EACH / ENDFOR Iterate over all elements of an array (free-format only). Syntax: FOR-EACH element IN array; ... ENDFOR;. Also supports %LIST, %SPLIT, and %SUBARR iterables: FOR-EACH name IN %LIST('A' : 'B' : 'C');, FOR-EACH part IN %SPLIT(str : ',');, and FOR-EACH val IN %SUBARR(arr : start : count); (iterates over a contiguous slice of a declared array; start is 1-based, count is optional - omit to iterate to end). The element variable receives a copy of each element - modifying it does not change the original. LEAVE and ITER work inside FOR-EACH just like in FOR loops. The operand after IN must be an array (a DIM standalone, data-structure, or parameter array) or one of %LIST / %SPLIT / %SUBARR; a scalar or other non-array operand is rejected at compile time with TRN2005 rather than silently skipping the loop.
ON-EXIT Procedure cleanup block (free-format only). Syntax: ON-EXIT [indicator]; ... END-PROC;. Placed at the end of a subprocedure before END-PROC. The cleanup code runs on any exit path - normal return, exception, or cancellation. The optional indicator is set *OFF for normal exit and *ON for abnormal exit. Return values are preserved across the cleanup block.
RETURN Return from program/procedure
COMP Compare (sets resulting indicators)
EXSR Execute subroutine
BEGSR / ENDSR Subroutine boundaries
Z-ADD Zero and add (numeric assignment). Result is truncated toward zero to its scale; the (H) extender rounds half away from zero (half-adjust) instead.
Z-SUB Zero and subtract (numeric negation: Result = 0 − Factor2). Supports the (H) half-adjust extender (round half away from zero).
SQRT Square root (Result = sqrt(Factor2); uses double arithmetic, rounds to result field's scale)
CAT Concatenate two character strings (Factor1 + optional blanks + Factor2; supports (P) pad extender and :n blank count in Factor2)
SUBST Substring extraction (Factor1=optional length, Factor2=source:start; supports (P) pad extender; without pad, preserves tail of result field). A start position outside the source, or a length running past its end, raises status 00100 - see Out-of-range positions on the string opcodes.
ADD Add (accumulate or sum two operands). Supports the (H) half-adjust extender (round the result half away from zero instead of truncating).
SUB Subtract (with optional Factor1; mirrors ADD). Supports the (H) half-adjust extender.
MULT Multiply. Supports the (H) half-adjust extender (round the product half away from zero instead of truncating).
DIV Divide. The quotient is truncated toward zero to the result field's scale; the (H) extender rounds half away from zero (half-adjust) instead. Do not combine (H) with a following MVR - MVR needs the truncated quotient's remainder.
MOVE Move right-justified: a longer source has its excess high-order digits/characters truncated; a shorter source overlays only the low-order positions and the result keeps its high-order positions - unless the (P) pad extender clears them (see MOVE Operation Extenders)
MOVEL Move left-justified: a longer source has its excess low-order digits/characters truncated; a shorter source overlays only the high-order (leftmost) positions and the result keeps its low-order positions - unless the (P) pad extender clears them (see MOVEL Operation Extenders)
XFOOT Sum all elements of a numeric array
TAG Label declaration (branch target for GOTO). May sit inside an IF, DOW, DOU, FOR, SELECT, or MONITOR block - see GOTO and TAG Around Structured Blocks
GOTO Branch to TAG label (unconditional or conditioned), including into or out of a structured block
SETON Set indicators on (numbered 01-99, LR, RT, control level L1-L9, halt H1-H9, external U1-U8, and overflow OA-OG/OV)
SETOFF Set indicators off (numbered 01-99, LR, RT, control level L1-L9, halt H1-H9, external U1-U8, and overflow OA-OG/OV)
CLEAR Set variable, indicator, or record format to type default (0, blanks, *OFF) - ignores INZ. For a data structure - including a plain, non-qualified named DS - clears every subfield to its type default. For a DISK record-format target the optional *NOKEY and *ALL qualifiers apply (see "CLEAR and RESET on Record Formats" below). For an OCCURS DS, clears the current occurrence; with *ALL, clears all occurrences and resets occurrence level to 1 (see "OCCURS Keyword" section).
RESET Restore variable to initialization value (INZ value or type default). For a DISK record-format target the optional *NOKEY and *ALL qualifiers apply (see "CLEAR and RESET on Record Formats" below).
SETLL Position disk file cursor at first record with key >= factor1; EQ indicator on when exact match found
READE Read next disk file record with matching key; EQ indicator on at end-of-file
CHAIN Random-by-key file lookup (disk files) or read-by-RRN (subfile records); the HI resulting indicator (cols 71-72 in fixed format) is set ON when the record is not found, OFF when fetched. An error indicator in the LO position (cols 73-74) traps an I/O error (set ON + %STATUS, execution continues, INFSR suppressed - same precedence as (E)), and is set OFF on a successful or not-found CHAIN
NEXT Select the program device for the next read of a multiple-device WORKSTN file. Factor 1 = program device, Factor 2 = WORKSTN file; supports the (E) extender. Compiles as a no-op against rpgc's single-device runtime (see "NEXT - Select the Next Program Device").
POST Refresh the INFDS feedback area for a file. Factor 1 = optional device name (WORKSTN), Factor 2 = file name. Accepted as a no-op - INFDS subfields are already populated by each I/O operation.
ACQ Acquire a program device for a multi-device WORKSTN file. Factor 1 = device name, Factor 2 = WORKSTN file. Accepted as a no-op on the single-device runtime.
REL Release a previously acquired program device. Factor 1 = device name, Factor 2 = WORKSTN file. Accepted as a no-op on the single-device runtime.
KLIST Define a named composite key. Factor 1 = key-list name. Must be immediately followed by one or more KFLD lines. The KLIST name can then be used as the search argument in CHAIN, SETLL, or SETGT.
KFLD Define one field of a composite key. Result = field name. Must immediately follow a KLIST or another KFLD.
LOOKUP Array element search. Factor 1 = search argument, Factor 2 = array name (optionally with index variable, e.g. ARR(IDX)). Resulting indicators: EQ (cols 75-76) set ON when exact match found; HI (cols 71-72) set ON when an element greater than the search argument is found; LO (cols 73-74) set ON when an element less than the search argument is found. HI and LO require ASCEND or DESCEND on the array declaration. When EQ+HI or EQ+LO are combined, exact match takes priority - if found, EQ is ON and HI/LO is OFF; if no exact match, the HI/LO search runs. The index variable is updated to the position of whichever search succeeded (1-based), or reset to 1 on complete miss. Arrays of every numeric type are searchable - packed, zoned, integer, unsigned and float - and order by numeric value, with an UNS(20) array ordering by its full unsigned magnitude.
EXCEPT Calculation-time output. Triggers E-type O-spec records. Factor 2 = exception group name (matches the EXCEPT name in O-spec cols 30-39). Blank Factor 2 outputs unnamed E-type records. Conditioning indicators on output records - including their AND/OR continuation lines - are respected: the record line plus its AND continuations form one AND-group, each OR continuation starts another, and the record is written when any group is fully satisfied (OR of AND-groups). Works in both cycle programs and non-cycle programs with PRINTER F-specs.
SORTA Sort an array in place. Default sort order is ascending. When the array is declared with DESCEND, SORTA sorts in descending order. Supports partial sort via SORTA %SUBARR(arr : start : count). The ASCEND and DESCEND keywords on DCL-S / D-spec control sort direction. Numeric arrays - packed, zoned, integer, unsigned and float - order by numeric value. Character arrays collate under the EBCDIC sequence (see Character Comparison Semantics), so ['A1','a1','01'] ascends to ['a1','A1','01'].
DUMP Emit a formatted program dump to diagnostic output (see "DUMP Opcode" below)
SHTDN Test for system shut-down request; HI resulting indicator set ON if shutdown requested, OFF otherwise. On JVM, always OFF. In free-format, use %SHTDN BIF instead.
MVR Move remainder from preceding DIV into result field. Must immediately follow a DIV operation. Factor 1 and Factor 2 are blank. In free-format, use %REM instead.
BITON Set bits ON in a 1-byte character Result field (fixed-format only). Factor 2 selects the bits - see "Bit Manipulation" below. In free-format, use %BITOR.
BITOFF Set bits OFF in a 1-byte character Result field (fixed-format only). Factor 2 selects the bits - see "Bit Manipulation" below. In free-format, use %BITAND/%BITNOT.
TESTB Test the bits of a 1-byte character Result field against Factor 2 (fixed-format only). Resulting indicators: HI (71-72) ON when every selected bit is OFF; LO (73-74) ON when the selected bits are mixed (some ON, some OFF); EQ (75-76) ON when every selected bit is ON. Factor 2 selects the bits - see "Bit Manipulation" below.
TESTN Test a character Result field for numeric content (fixed-format only). Factor 2 is blank. Resulting indicators: NU (numeric), NZ (numeric with leading blanks - treated as numeric), BL (all blanks).
TESTZ Test the zone of the leftmost character of a Result field (fixed-format only). Factor 2 is blank.

Bit Manipulation (BITON / BITOFF / TESTB)

Factor 2 selects which bits BITON, BITOFF, and TESTB act on. Bits are numbered 0 - 7 from the leftmost (high-order) bit. Factor 2 accepts any of:

A Factor 2 that resolves to none of these (an undefined name) is rejected with TRN1048 rather than silently treated as a no-op.

Hexadecimal Literals

Outside the bit operations above (where X'nn' is a raw mask), a hexadecimal literal has the same meaning as the corresponding character literal, exactly as on IBM i: its digits are the character data's raw bytes, and they are never CCSID-converted. Those bytes are read under the target field's own CCSID, not the job's - a hexadecimal literal names bytes, and what they mean is the page of the field they land in. X'E1' is therefore ß in a CCSID(850) field and á in a CCSID(819) one, while in a field with no CCSID keyword it is read under the job page as before. X'C1C2C3C4C5' is 'ABCDE'; a field initialized with INZ(X'04') holds the byte x'04', so supplying that field as a bit-op mask selects bit 5, a zone move from it transfers zone 0, and comparing it to a blank (x'40') is false. DSPLY of data containing a byte with no printable glyph shows a blank in that position - matching how a 5250 display renders it - while the underlying data keeps the exact byte value.

EVAL Operation Extenders

EVAL (and free-format assignment) accepts these operation extenders, which control the precision of the numeric assignment:

Float Operands in Arithmetic Expressions

An arithmetic expression (+, -, *, /) is typed by its operands, and float is contagious: if either operand is FLOAT, the expression is a float expression, evaluated in floating point rather than fixed point. A mixed expression such as floatField * packedField is therefore float - the packed operand widens, rather than pulling the result down to a decimal type.

The result type is what a directly-nested %CHAR renders, so a float expression prints its float external representation (scientific notation), not a fixed-decimal image:

dcl-s a float(8) inz(2.5);
dcl-s b float(8) inz(4.0);
dsply %char(a * b);          // +1.000000000000000E+001

Assigning the expression to a fixed-point target is unaffected - the value converts on the store, so packedResult = a * b with two decimals gives 10.00 as usual. Exponentiation (**) stays on the fixed-point path.

CLEAR and RESET on Record Formats

When CLEAR or RESET names a record format of an externally described DISK file, the optional *NOKEY and *ALL qualifiers decide which DDS fields are affected:

Form Effect on a DISK record format
CLEAR rec / RESET rec no-op - no DDS field is touched
CLEAR *NOKEY rec / RESET *NOKEY rec no-op - no DDS field is touched
CLEAR *ALL rec / RESET *ALL rec every DDS field reset (keys included)
CLEAR *NOKEY *ALL rec / RESET *NOKEY *ALL rec keys preserved; non-keys reset

DDS fields of a DISK record format are implicitly classified as input-only for CLEAR's and RESET's purposes, so plain CLEAR / RESET (and *NOKEY alone) do nothing. *ALL forces every field to be reset; *NOKEY then excludes the key columns. When both qualifiers are specified *NOKEY must precede *ALL. CLEAR resets each field to its data-type default (blanks for character, zero for numeric); RESET restores each field to its initialization value, which is the same data-type default when no *INZSR or DDS default applies.

DSPLY Numeric Rendering Contract

When DSPLY's factor 1 is a non-float numeric field (packed, zoned, integer, unsigned), the value is rendered to the workstation as its raw digit image:

Examples:

Field Value Image
PACKED(5:0) 7 ␣␣␣␣7
PACKED(5:0) -7 ␣␣␣␣7-
PACKED(5:0) 0 ␣␣␣␣0
PACKED(7:2) 12345.67 1234567
PACKED(7:2) 12.34 ␣␣␣1234
PACKED(7:2) -12.34 ␣␣␣1234-
INT(5) 42 ␣␣␣42
INT(10) 123456 ␣␣␣␣123456
UNS(20) 18446744073709551615 18446744073709551615

( denotes a literal space.) Character fields pass through unchanged and keep their full declared length: a fixed-length CHAR(n) field displays all n characters, including trailing blanks, matching IBM i (a CHAR(20) holding 'Hello' displays Hello followed by 15 blanks). A varying (VARCHAR) field displays only the characters it currently holds. An indicator operand (*INxx, or a named indicator) displays its one-character value - 1 when on, 0 when off - because an indicator is a one-byte character field. Wrapping a numeric in %CHAR(...) deliberately bypasses this contract and produces the minimal-width decimal string %CHAR is defined to return.

DSPLY in WORKSTN Programs

When a program that owns a WORKSTN display file runs DSPLY, rpgc suspends the display and presents a full-screen Display Program Messages panel on a 24×80 screen:

The panel pauses for that acknowledgment only when a display format has already been shown at the workstation this session (a display file with RSTDSP(*NO) has displayed a format). A DSPLY issued before any format is shown, or in a batch/no-display program, posts its message and continues without pausing, so non-interactive runs never block waiting for an operator.

Successive DSPLY messages accumulate for the whole program call: each panel lists every message issued so far - one per line from row 4 downward - not just the latest. This holds even when an EXFMT of a display format runs between two DSPLY operations; the second panel still shows the earlier messages above the new one. The accumulated queue is reset only when the program ends or transfers control to another program (EXTPGM call/return). If the content area fills, the panel scrolls to the most recent messages.

When an EXFMT drawn with OVERLAY follows a dismissed panel, the underlying display-file image is restored and only the Display Program Messages title bleeds through at row 1 - the panel's body (job-start line, messages, prompt, and key legend) is cleared rather than left under the new format.

DUMP Opcode

The DUMP opcode emits a formatted program dump listing all program fields, indicators, and their current values. On IBM i, this output goes to the QPPGMDMP spool file; on the JVM target, it is written to System.err (standard error) so it does not interfere with DSPLY output on stdout.

Free-format syntax:

DUMP(A);            // always dump
DUMP;               // dump (always executes - DEBUG H-spec has no effect)

Fixed-format syntax:

Factor 1 Opcode Factor 2 Result
identifier DUMP (blank) (blank)

The optional identifier (a character literal, field name, or named constant) tags the dump output so multiple DUMP calls in a program can be distinguished.

The (A) operation extender forces the dump to execute regardless of the DEBUG control-spec keyword. The DEBUG keyword has no effect in Triton RPG - DUMP always executes, whether or not (A) is specified.

SHTDN Opcode / %SHTDN BIF

The SHTDN opcode tests whether a controlled shutdown has been requested for the job, subsystem, or system. On IBM i, the HI resulting indicator (columns 71-72) is set ON when a shutdown is in progress. On the JVM target, no shutdown concept exists, so the indicator is always set to OFF.

Fixed-format syntax:

Factor 1 Opcode Factor 2 Result HI (71-72)
(blank) SHTDN (blank) (blank) indicator

Free-format: Use the %SHTDN built-in function, which returns *ON if a shutdown has been requested, *OFF otherwise. On JVM, always *OFF.

if %shtdn;
  // perform cleanup
endif;