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 part | Columns | Free-form equivalent |
|---|---|---|
| Factor 1 | 12 to 25 | The left operand or the search argument |
| Opcode and extender | 26 to 35 | The opcode, or an operator such as +, -, * and / |
| Factor 2 | 36 to 49 | The right operand or the file name |
| Result field | 50 to 63 | The target of an assignment |
| Resulting indicators | 71 to 76 | A 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-format | Target afterwards | Free-form |
|---|---|---|
MOVEL (P) | ABC | tgt = src; |
MOVEL | ABC45 | %subst(tgt : 1 : %len(src)) = src; |
MOVE | 12ABC | %subst(tgt : %len(tgt) - %len(src) + 1 : %len(src)) = src; |
MOVE (P) | ABC | evalr 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
MOVELto=without checking the target length. The rest of the target is blanked, not kept. - Converting
MOVEto=.MOVEaligns on the right, and=aligns on the left. - Leaving a result indicator in place and testing
*IN50. Replace it with%found,%eofor%equal. - Converting
DIVandMVRseparately, so the remainder no longer matches the quotient. - Changing the parameter order when converting
PARMlines 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,%equaland%error. - Treat
MOVEandMOVELas 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.