Fixed-Format to Free-Format Opcode Mapping

This page converts old fixed-format calculation lines into free-form RPGLE, one opcode at a time. Each section shows the fixed-format line and the free-form statement that replaces it, so you can translate source line by line without changing what the program does.

It is a mapping, not a lesson. For what the operations mean, use the RPGLE Opcode Reference. For how calculation specifications and logic blocks fit together, read RPGLE Calculation Specs and Logic Blocks. For the three source styles, see RPGLE Fixed, Free and Fully Free Specifications. The fixed-format lines below are for recognition. Do not paste them into a free-form member.

How a fixed-format line maps

A fixed-format calculation line puts each part in a fixed column range. A free-form statement has no column rules and ends with a semicolon.

Fixed-format partColumnsFree-form equivalent
Factor 112 to 25The left operand or the search argument
Opcode and extender26 to 35The opcode, or an operator such as +, -, * and /
Factor 236 to 49The right operand or the file name
Result field50 to 63The target of an assignment
Resulting indicators71 to 76A built-in function such as %found, %eof, %equal or %error

The ruler for the examples is:

CL0N01Factor1+++++++Opcode(E)+Factor2+++++++Result++++++++Len++D+HiLoEq..

Assignment and arithmetic

Fixed-format arithmetic names the operation and puts the answer in the result field. In free-form the same work is an expression, and Z-ADD is plain assignment.

C                   Z-ADD     0             TOTAL
C                   ADD       AMOUNT        TOTAL
C                   SUB       5             TOTAL
C     QTY           MULT      PRICE         EXTENDED
C     TOTAL         DIV       COUNT         AVG
C     17            DIV       5             QUOTIENT
C                   MVR                     REST
total = 0;                      // Z-ADD 0 TOTAL
total += amount;                // ADD AMOUNT TOTAL
total -= 5;                     // SUB 5 TOTAL
extended = qty * price;         // MULT
avg = total / count;            // DIV
quotient = %div(17 : 5);        // DIV with an integer result
rest = %rem(17 : 5);            // MVR

Two details matter. MVR must follow the DIV whose remainder it returns, so convert the pair together: %div gives the quotient and %rem gives the remainder. A fixed-format (H) extender rounds the result, and the free-form equivalent is eval(h) in front of the assignment.

Control flow

The structured opcodes keep their names. What changes is that the condition is written as an expression, and a counted DO becomes for.

C                   IF        TOTAL > 100
C                   EVAL      BAND = 'HIGH'
C                   ELSE
C                   EVAL      BAND = 'LOW'
C                   ENDIF
C     1             DO        5             I
C                   ADD       I             SUM
C                   ENDDO
C                   DOW       N < 3
C                   ADD       1             N
C                   ENDDO
C                   DOU       N >= 3
C                   ADD       1             N
C                   ENDDO
C                   SELECT
C                   WHEN      TOTAL >= 100
C                   EVAL      BAND = 'HIGH'
C                   WHEN      TOTAL >= 10
C                   EVAL      BAND = 'MID'
C                   OTHER
C                   EVAL      BAND = 'LOW'
C                   ENDSL
if total > 100;
  band = 'HIGH';
else;
  band = 'LOW';
endif;

for i = 1 to 5;
  sum += i;
endfor;

dow n < 3;
  n += 1;
enddo;

dou n >= 3;
  n += 1;
enddo;

select;
  when total >= 100;
    band = 'HIGH';
  when total >= 10;
    band = 'MID';
  other;
    band = 'LOW';
endsl;

dow tests before the first pass and dou tests after it, so a dou body always runs at least once. The older IFGT, IFEQ and DOWLT forms with a comparison in the opcode become the same if and dow with the comparison in the expression.

File operations and resulting indicators

File opcodes keep their names. What goes is the resulting indicator in columns 71 to 76. Test the outcome by name instead.

C     KEYVAL        CHAIN     ORDERF                             50
C                   IF        *IN50
C                   EVAL      MSG = 'Not found'
C                   ENDIF
C     *LOVAL        SETLL     ORDERF
C                   READ      ORDERF                                 99
C                   DOW       NOT *IN99
C                   ADD       1             RECORDS
C                   READ      ORDERF                                 99
C                   ENDDO
C     KEYVAL        SETLL     ORDERF                                 98
chain keyVal orderFile;
if not %found(orderFile);
  msg = 'Not found';
endif;

setll *loval orderFile;
read orderFile;
dow not %eof(orderFile);
  records += 1;
  read orderFile;
enddo;

setll keyVal orderFile;
isEqual = %equal(orderFile);

In the old code the indicator on a CHAIN is on when the record is not found, so *IN50 becomes not %found(orderFile). The EOF indicator on READ becomes %eof, and the EQ indicator on SETLL becomes %equal. If the old code used an error indicator, add the (e) extender and test %error. A key list also changes form:

C     ORDKEY        KLIST
C                   KFLD                    KEYCUST
C                   KFLD                    KEYORD
C     ORDKEY        CHAIN     ORDERF                             50
chain (keyCust : keyOrd) orderFile;

For an externally described file you can give the composite key as a list of values in parentheses, or build it with %kds from a data structure. See the RPGLE File I/O Opcodes Reference for the syntax of each file operation, and the RPGLE Opcode Reference for the others.

Calls and parameters

CALL, PARM and PLIST are replaced by a prototype and an ordinary call. The program name moves into the prototype, and each PARM becomes a parameter.

C                   CALL      'DISCPGM'
C                   PARM                    AMOUNT
C                   PARM                    DISCOUNT
dcl-pr discPgm extpgm('DISCPGM');
  orderAmount packed(9 : 2);
  discountAmount packed(9 : 2);
end-pr;

discPgm(amount : discount);

A *ENTRY PLIST in the called program becomes a procedure interface, dcl-pi *n; ... end-pi;, with the same parameters in the same order. Keep the order and the data types identical on both sides.

MOVE and MOVEL

MOVE and MOVEL are the opcodes most often converted wrongly. In free-form there is no direct opcode, and IBM says to use EVAL, EVALR or a built-in function instead. They are not the same as = when the two character fields differ in length.

The table uses a three-character source ABC and a five-character target that starts as 12345.

Fixed-formatTarget afterwardsFree-form
MOVEL (P)ABCtgt = src;
MOVELABC45%subst(tgt : 1 : %len(src)) = src;
MOVE12ABC%subst(tgt : %len(tgt) - %len(src) + 1 : %len(src)) = src;
MOVE (P)ABCevalr tgt = src;
tgt = '12345';
tgt = src;

tgt = '12345';
%subst(tgt : 1 : %len(src)) = src;

tgt = '12345';
%subst(tgt : %len(tgt) - %len(src) + 1 : %len(src)) = src;

evalr tgt = src;

When the source is longer than the target, MOVEL drops the excess rightmost characters and MOVE drops the excess leftmost characters. When MOVE or MOVEL changes the type, for example character to numeric or to a date, use the conversion functions instead: %dec and %int in the RPGLE Numeric and Conversion BIFs Reference, and %date and %char in the RPGLE Date and Time BIFs Reference.

Common mistakes

  • Converting MOVEL to = without checking the target length. The rest of the target is blanked, not kept.
  • Converting MOVE to =. MOVE aligns on the right, and = aligns on the left.
  • Leaving a result indicator in place and testing *IN50. Replace it with %found, %eof or %equal.
  • Converting DIV and MVR separately, so the remainder no longer matches the quotient.
  • Changing the parameter order when converting PARM lines to a prototype.

FAQ

Is MOVE the same as assignment?

No. = copies from the left and pads the rest of the target with blanks. MOVEL also copies from the left, but without (P) it leaves the rest of the target unchanged. MOVE copies from the right.

How do I replace CALL and PARM?

Declare a prototype with dcl-pr, naming the program with extpgm, and list the parameters in the same order as the PARM lines. Then call it by name with the arguments in parentheses.

Do I need **FREE to use these statements?

Only for fully free source. A fixed-format member can still hold free-form statements in a free-form block, but a member that starts with **free is the usual choice for new code. The fixed-format lines on this page cannot go into a fully free member.

Summary

  • Convert one operation at a time, and keep the logic the same until the program passes its tests.
  • Replace result indicators with %found, %eof, %equal and %error.
  • Treat MOVE and MOVEL as length-dependent, and use the table above.
  • Look up any opcode in the RPGLE Opcode Reference, and any function in the RPGLE Built-in Functions Reference.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top