| 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 bit-number literal - one to eight digits
0-7in apostrophes, e.g.'05'selects bits 0 and 5. - A hexadecimal literal -
X'nn', whose byte value is the mask directly, e.g.X'04'selects bit 5. - A one-byte character field - the field's byte value is the mask.
- A named constant holding either a bit-number character value or a hex value. A named constant behaves exactly as the equivalent literal does.
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:
(H)- half-adjust (round) the final result to the target's decimal positions instead of truncating.(M)- use maximum (default) intermediate precision rules.(R)- keep the decimal positions of every intermediate result from being reduced below the result field's decimal places. Without(R), the default precision rules can shrink an intermediate's decimals (even to zero) when the integer part is large, losing fractional precision;(R)floors each intermediate's scale at the result's decimal places. For example, witheval(r) result = a * bwhereresulthas 4 decimals, thea * bintermediate keeps at least 4 decimals. When combined with(H)(i.e.eval(rh)), intermediates keep one extra decimal beyond the result so the final half-adjust rounds on the correct digit.
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:
- The declared precision in right-aligned digit positions - leading zeros
are suppressed to blanks. The units (rightmost) position is preserved so a
zero balance still shows a single
0. - No decimal point, regardless of the field's scale - the digits are shown
unedited (a
PACKED(7:2)holding12345.67displays1234567). - No leading sign. A negative value appends a trailing
-as the rightmost character, making its image one position wider than the declared precision. - Unsigned fields render their full unsigned magnitude. An
UNS(20)field is never negative, so it never carries a trailing sign - even a value at or above 2^63 (whose bit pattern would be a negative signed integer) shows its full unsigned decimal (UNS(20)at its maximum displays all 20 digits).
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 screen is cleared first - the previous format does not bleed through.
- Row 1 holds the centered
Display Program Messagestitle. - Row 3 holds a job-start line (
Job .../.../... started on ... at ... in subsystem ...). Because a JVM process is not an IBM i job, its identifiers (job number, user, device, time) reflect the rpgc runtime session rather than a native job, so this line's values differ from run to run. - Each
DSPLYmessage renders from row 4 downward asDSPLY <value>, using the value contract described above. - Row 19 shows
Press Enter to continue.and row 23 showsF3=Exit F12=Cancel. The program resumes when the operator presses Enter.
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;