MC Press Online

Sunday, May 28th

Last updateThu, 25 May 2017 10am

You are here: Home ARTICLES Programming RPG Exploit Dynamic Program-Storage Management in ILE RPG

Programming / RPG

Exploit Dynamic Program-Storage Management in ILE RPG

Support MC Press - Visit Our Sponsors


Evolve Your RPG Coding: Move from OPM to ILE ... and Beyond



Click for this Month's

Bookstore Special Deals

Allocate and free up program storage at run time.


Many i5/OS APIs receive variable-length output parameters. Many i5/OS machine interface (MI) instructions expect variable-length output operands. Many algorithms implemented by user programs need to allocate program storage with unpredictable length until run time. All these facts lead to the need for allocating and freeing program storage at run time.


Here, I will discuss four dynamic program-storage management methods that you can use in ILE RPG.

  • Dynamically allocating automatic storage by MI instruction MODASA
  • Managing heap storage with ILE CEE APIs or heap management MI instructions
  • Using teraspace storage in ILE RPG
  • Using thread-level global dynamic storage via Pthreads thread-specific storage APIs

Dynamically Allocating Automatic Storage by MI Instruction MODASA

On i5/OS, automatic storage supplied to a program is called the "automatic storage stack." Before threads were introduced to i5/OS, the automatic storage stack of a program was allocated based on the program's activation group. After threads were introduced to i5/OS, the automatic storage stack of a program became allocated based on the thread in which the programming is running. There are two automatic storage segments within a thread—one for system-state programs and the other for user-state programs—and the two segments reside, respectively, in the system domain and the user domain. A user program has space addressability to the user-domain automatic storage stack. An interesting example of addressing a program's automatic storage stack is provided in Appendix A, Changing a Caller's Automatic Variables.


Unlike common operating systems, where automatic storage allocation in a procedure is decided at compile time by reserving space on the stack, an i5/OS program can modify (extend or truncate) automatic storage allocation in a certain procedure invocation at run time.


So what's the benefit of using dynamically allocated automatic storage over other run-time storage allocation methods? First, automatic storage is local to a procedure: you can forget it once the control flow leaves the procedure; there's no need to release or re-initialize the allocated automatic storage. Second, automatic storage is a thread-local resource, which means that it needs no synchronization in multi-threaded environments.


The MI instruction Modify Automatic Storage Allocation (MODASA) is responsible for extending and truncating automatic storage in a specific invocation of a procedure. This instruction's bound program access interface (system built-in for HLLs), _MODASA, does not provide the functionality to truncate allocated automatic storage. Here is the prototype of system built-in _MODASA.


     /* MODASA, modify automatic storage allocation */

     /* Note that unlike MI instruction MODASA, built-in _MODASA */

     /* cannot be used to truncate ASF */

     d modasa          pr              *   extproc('_MODASA')

     d     mod_size                  10u 0 value


_MODASA returns a space pointer addressing the allocated automatic storage. Operand mod_size should be greater than 0 and less than or equal to 16,773,119. When the automatic storage is extended, the extension is aligned on a 16-byte boundary. The extension is not initialized.


Look at this example ILE RPG program, T032, that uses dynamically allocated automatic storage as the receiver operand to MI instruction Materialize Authorized Users (MATAUU). With option hex 32, instruction MATAUU returns privately authorized user profiles to the MI object identified by the object operand. Here's the prototype of system built-in _MATAUU for ILE RPG.


     /* MATAUU, materialize authorized users */

     d matauu          pr                  extproc('_MATAUU')

     d     receiver                    *   value

     d     object                      *

     d     option                     1a


ILE RPG program T032 lists privately authorized user profiles to a given program object.



      * @file t032.rpgle


      * test of MATAUU.

      * retrieve privately authorized USRPRF to a PGM object



      /copy mih52


     d mat_opt         s              1a   inz(x'32')

     d tmpl            ds                  likeds(matauu_tmpl_t)

     d                                     based(tmpl_ptr)

     d tmpl_ptr        s               *

     d authd           ds                  likeds(auth_desc_long_t)

     d                                     based(authd_ptr)

     d authd_ptr       s               *


     d dec10           s               *

     d len             s             10i 0

     d ind             s             10i 0


     d i_main          pr                  extpgm('T032')

     d     pgm_name                  10a


     d i_main          pi

     d     pgm_name                  10a




           rslvsp_tmpl.obj_type = x'0201';

           rslvsp_tmpl.obj_name = pgm_name;

           rslvsp2(dec10 : rslvsp_tmpl);


           // materialize privately authorized profiles

           // using long description entry format

           mat_opt = x'32';


           // how many bytes do we need to allocate? (1)

           tmpl_ptr = modasa(8);

           tmpl.bytes_in = 8;

           matauu(tmpl_ptr : dec10 : mat_opt);

           len = tmpl.bytes_out;


           // really allocate receiver buffer (2)

           tmpl_ptr = modasa(len);

           tmpl.bytes_in = len;

           matauu(tmpl_ptr : dec10 : mat_opt);


           // report returned privately authorized users

           authd_ptr = tmpl_ptr + 16;

           for ind = 1 to tmpl.num_private_users;

               dsply 'Privately Authed User' '' authd.usrprf_name;

               authd_ptr += AUTH_DESC_LONG_LENGTH;



           *inlr = *on;



Notes in code:

(1) Allocate 8 bytes for MATAUU's operand 1, and then call MATAUU to retrieve the necessary amount of bytes to allocate.

(2) Allocate necessary amount of bytes for MATAUU's operand 1.


To test ILE RPG program T032, you might grant specific authority for a program object to a given user profile and then call T032 and pass the name of the program object. Here's an example:



4 > GRTOBJAUT OBJ(T031) OBJTYPE(*PGM) USER(DEMO) AUT(*USE)                

    Authority given to user DEMO for object T031 in LSBIN object type *PGM.

    Object authority granted.                                        


    Authority given to user LJL2 for object T031 in LSBIN object type *PGM.

Object authority granted.                                        

4 > CALL PGM(T032) PARM('T031')        

    DSPLY  Privately Authed User    DEMO

  ? *N                                  

    DSPLY  Privately Authed User    DEMO

  ? *N                                 

    DSPLY  Privately Authed User    LJL2

  ? *N                                 

    DSPLY  Privately Authed User    LJL2

  ? *N                                 


Managing Heap Storage with ILE CEE APIs or Heap Management MI Instructions

Since heap storage is an activation group-level resource, it is also referred to as "activation group-based heap space storage" in MI documentation provided by IBM. The heap storage for an activation group is accessible by any thread with a program or procedure running in the activation group. A default heap space with heap identifier 0 is automatically available in each activation group. The default heap space is created when the first Allocate Activation Group-Based Heap Space (ALCHSS) instruction is issued against the default heap space. A program can create additional heap spaces in its activation group by issuing MI instruction Create Activation Group-Based Heap Space (CRTHS). The amount of heap space storage that can be allocated for a single heap space is 4G-512K bytes.


To illustrate heap space, the following table shows the attributes of an activation group's default heap space and the acceptable values of each heap space attribute.


Heap Space Attributes and Values

Heap Attribute

Value of an Activation Group's Default Heap Space

Acceptable Values

Maximum single allocation size

(16M - 1 page) bytes *

(16M - 1 page) bytes *

Minimum boundary requirement

16-byte boundary

Min: 16 bytes

Creation size advisory

1 page bytes

Min: 1 page bytes;

Max: (16M - 1 page) bytes

Extension size advisory

1 page bytes

Min: 1 page bytes;

Max: (16M - 1 page) bytes


Domain is determined from the state of the program issuing the instruction



Allocation strategy

Normal allocation strategy

Normal allocation strategy;

Force process space creation on each allocate

Allow heap space mark **

A heap space mark is not allowed

Allow heap space mark;

Prevent heap space mark

Transfer size

1 page bytes

Transfer the minimum storage transfer size for this object;

Transfer the machine default storage transfer size for this object

Create the heap space in the PAG

The process access group (PAG) membership advisory value is taken from the activation group

Do not create the heap space in the PAG;

Create the heap space in the PAG

Allocation initialization

Heap space storage allocations are not initialized to the allocation value.

Do not initialize allocations;

Initialize allocations

Overwrite freed allocations

Heap space storage allocations are not overwritten to the freed value after being freed.

Do not overwrite freed allocations;

Overwrite freed allocations


* The term "page" is defined to mean one or more basic storage units used by the machine to manage memory and DASD. The number of bytes in a page can be determined with option hex 12 of the Materialize Resource Machine Data (MATRMD) instruction. The typical value of 1 page is 4KB (4096).


** If a heap space mark is allowed, marking a heap space allows a subsequent Free Activation Group-Based Heap Space Storage from Mark (FREHSSMK) instruction or ILE CEE API Release Heap (CEERLHP), using the mark identifier returned in operand 1, to free all outstanding allocations that were performed against the heap space since the heap space was marked with that mark identifier. This relieves the user from performing a Free Activation Group-Based Heap Space Storage (FREHSS) instruction or ILE CEE API Free Storage (CEEFRST) for every individual heap space allocation.


The ILE RPG language provides operation codes and built-in functions to operate the default heap space of an activation group.


Opcodes and BIFs


Operation Code

Language Built-in Function

Allocate Storage



Free Storage


(No corresponding BIF)

Reallocate Storage




To gain full control over heap spaces, use ILE CEE heap APIs or heap management MI instructions. The following table lists ILE CEE heap APIs or heap management MI instructions that achieve same functionalities.


Heap Space Equivalents



Heap Management MI Instructions

Create a new heap



Destroy an existing heap



Allocate storage within a heap



Free one previously allocated heap storage



Change the size of previously allocated storage



Mark a heap



Free heap storage since the mark was specified




For ILE RPG prototypes of heap management MI instructions, please refer to mih52.rpgleinc, ILE RPG header for MI instructions (system built-ins).


There are no real differences in functionality between ILE CEE heap APIs and heap management MI instructions. One can even mix them when working with heap storage.


Please note the following when creating a heap space that is allowed to mark:

  • No heap identifier is required for CEERLHP or FREHSSMK. The heap identifier of the heap to be released is inferred from the mark address. The release operation may be issued from an activation group other than the activation group that owns the heap.
  • Multiple marks can be maintained for each heap. Mark and release operations treat the heap in a fashion similar to a stack. A release operation frees all storage allocated after the specified mark operation, but not the storage allocated before the specified mark operation. For example, in the following ILE RPG program, T041, heap storage allocated before the target heap was marked with space pointer mark2 could not be released when issuing instruction FREHSSMK on the target heap with mark_identifier operand set to mark2.



      * @file t041.rpgle


      * test of heap management instructions



      /copy mih52


     d CEEGTST         pr

     d     heap_id                   10i 0

     d     size                      10i 0

     d     ptr                         *

     d     fc                        12a   options(*omit)


     d crt_tmpl        ds                  likeds(crths_tmpl_t)

     d                                     based(crt_tmpl_ptr)

     d crt_tmpl_ptr    s               *

     d heap_id         s             10i 0

     d mark            s               *

     d mark2           s               *

     d len             s             10i 0

     d ptr             s               *

     d buf             ds            32    based(ptr)

     d ptr2            s               *

     d buf2            ds            32    based(ptr2)




           // create a new heap space by instruction CRTHS

           crt_tmpl_ptr = modasa(96);

           propb(crt_tmpl_ptr : x'00' : 96);

           crt_tmpl.max_alloc  = x'FFF000';  // 16M - 1 page

           crt_tmpl.min_bdry   = 16;

           crt_tmpl.crt_size   = 0;          // use system default value

           crt_tmpl.ext_size   = 0;          // use system default value

           crt_tmpl.domain     = x'0000';    // system should chose the domain

           crt_tmpl.crt_option = x'2CF1F0000000';

               // normal allocation strategy

               // allow heap space mark

               // transfer the machine default storage transfer size

               // do not create the heap space in the PAG

               // initialize allocations

               // overwrite freed allocations

               // allocation value, '1'

               // freed value, '0'

           crths(heap_id : crt_tmpl);


           // mark heap space

           sethssmk(mark : heap_id);


           // allocate heap storage

           len = x'A00000';         // 10MB

           CEEGTST(heap_id : len : ptr : *omit);


           sethssmk(mark2: heap_id);


           CEEGTST(heap_id : len : ptr2: *omit);


           // free heap storage by mark

           frehssmk(mark2);       // heap storage pointed to by ptr2 is freed

           buf = 'ptr is NOT freed!';  // heap storage pointed to by ptr is NOT freed!


           frehssmk(mark);        // heap storage pointed to by ptr is freed


           // destroy heap space



           *inlr = *on;



Using Teraspace Storage in ILE RPG

A teraspace is a large, contiguous, process-local address space of 1TB size. A teraspace is not a space object, which means it cannot be referred to by using a system pointer, but it can be addressed with a space pointer. High-level languages (HLLs)—including ILE RPG, ILE COBOL, and ILE CL—are teraspace-enabled by default.


The following teraspace APIs exported by service program QSYS/QC2UTIL3 can be used to manage teraspace storage in an ILE RPG program.

  • _C_TS_malloc—This function reserves a block of teraspace storage of size bytes. Unlike the _C_TS_calloc() function, _C_TS_malloc() does not initialize all elements to 0.
  • _C_TS_calloc—This function reserves teraspace storage space for an array of num elements, each of length size bytes. The _C_TS_calloc()function then gives all the bits of each element an initial value of 0.
  • _C_TS_free()—This function frees a block of teraspace storage.
  • _C_TS_realloc—This function changes the size of a previously reserved teraspace storage block.


Here are the prototypes of teraspace APIs extracted from ts.rpgleinc.



      * The _C_TS_calloc() function reserves teraspace storage space for an

      * array of num elements, each of length size bytes. The _C_TS_calloc()

      * function then gives all the bits of each element an initial value of

      * 0.


     d ts_calloc       pr              *   extproc('_C_TS_calloc')

     d     num                       10i 0 value

     d     size                      10i 0 value



      * The _C_TS_free() function frees a block of teraspace storage.


     d ts_free         pr              *   extproc('_C_TS_free')

     d     ptr                         *   value



      * The _C_TS_malloc() function reserves a block of teraspace storage of

      * size bytes. Unlike the _C_TS_calloc() function, _C_TS_malloc() does

      * not initialize all elements to 0.


     d ts_malloc       pr              *   extproc('_C_TS_malloc')

     d     size                      10i 0 value



      * The realloc() function changes the size of a previously

      * reserved teraspace storage block.


     d ts_realloc      pr              *   extproc('_C_TS_realloc')

     d     ptr                         *   value

     d     size                      10i 0 value


The most notable benefit of using teraspace storage might be the large, contiguous address space. Limited by the single-level storage model, you cannot allocate a single contiguous piece of automatic storage or heap storage larger than 16MB. When you really need a contiguous space larger than 16M bytes returned by a single allocation, teraspace would be a good choice. Here's a simple example ILE RPG program that searches for a specific character through a 32M bytes teraspace allocation.


     h bnddir('QC2LE')


      /copy mih52

      /copy ts


     d ptr             s               *

     d val             ds                  qualified

     d                                     based(ptr)

     d     n                          9p 0

     d     str                        3a


     d pos             s               *

     d buf             ds             3    based(pos)




           // allocate 32MB teraspace storage

           ptr = ts_calloc(1 : 33554432);

           val.n = 95;

           val.str = 'ABC';


           // offset 16M bytes

           ptr += 16777216;

           val.n = 96;

           val.str = 'DEF';

           ptr -= 16777216;


           // search for character 'D'

           pos = memchr(ptr : 'D' : 33554432);

           if pos <> *NULL;

               dsply 'Get it!' '' buf;



           // free allocated teraspace storage



           *inlr = *on;



Using Thread-Level Global Dynamic Storage via Pthreads Thread-Specific Storage APIs

Some i5/OS programmers believe that ILE RPG is not a multi-thread-capable language. It's not accurate to say a programming language is multi-thread-capable or not. Most general-purpose programming languages don't have thread-synchronization-intrinsic primitives. For example, there are no synchronization primitives in C language's or C++ language's keywords. It is the operating system, not a specific programming language, that provides thread capabilities. Microsoft Windows provides thread capabilities to HLLs with thread management APIs such as CreateThread(), and POSIX-confirmed UNIX-like operating systems provide thread capabilities to HLLs with the Pthread library. It is not the duty of a programming language to implement thread support for different target operating systems. A programming language with large and often cross-platform function library or class library support might be an exception. For example, Java provides thread support with a unified interface under different platforms and leaves the complexity of working with platform-specific thread functions to vendors who implement the Java Virtual Machine (JVM) on a specific platform.


In my opinion, it would be more accurate to say that ILE RPG is unaware of multi-threading than to say that ILE RPG is not a multi-thread-capable language. Historically, the RPG compiler leaves a large number of static variables in compiled RPG modules or RPG programs. This makes it more complex to design a procedure or program to be invoked in a multi-threaded environment in ILE RPG than in other programming languages, such as C or C++. But, as an i5/OS HLL, there's no essential difference between ILE RPG and other i5/OS HLLs in terms of managing threads or implementing procedures or programs to run in multi-threaded environments.


In V6R1, a new value, THREAD(*CONCURRENT) was introduced to the THREAD parameter of ILE RPG's control specification. If THREAD(*CONCURRENT) is specified, then multiple threads can run the procedures within the module at the same time and be completely independent of each other. By default, all the static storage in the module will be in thread-local storage, meaning that each thread will have its own copy of the static variables in the module, including compiler-internal variables. But what if we need thread-level dynamically allocated storage that is global to all procedures within an i5/OS thread? The answer is Pthreads thread-specific storage APIs. This set of APIs provides us a mechanism to safely allocate, consume, and free dynamic thread-private storage that is global to all procedures within a thread.


Pthreads Thread-Specific Storage APIs


The i5/OS exposes thread management functions by implementing the POSIX standard for threads (Pthreads). Thread-specific storage APIs are one group of APIs of Pthread APIs.


Thread-Specific Storage APIs




Retrieves the thread local storage (TLS) value associated with the key; pthread_getspecific() may be called from a data destructor.


Creates a TLS key for the process and associates the destructor function with that key.


Deletes a process-wide TLS key.


Sets the TLS value associated with a key.


For ILE RPG prototypes of Pthread thread-specific storage APIs, please refer to pthread.rpgleinc.


Example ILE RPG Program Using Dynamically Allocated Thread-Specific Storage

ILE RPG program T038 starts two child threads in its main procedure that use different types of storage in different threads.



      * @file t038.rpgle


      * test of thread-specific APIs


      * CL command to run t038:





     h bnddir('QC2LE')


      /copy mih52

      /copy pthread.rpgleinc

      /copy ts.rpgleinc


     /* thread function */

     d thread_main     pr              *

     d     param                       *   value


     /* TLS destructor */

     d tls_destructor  pr

     d     tls                         *   value


     /* procedure that consumes allocated TLS storage */

     d func            pr

     d     tls_key                   10i 0 value


     d thd             ds                  likeds(pthread_t)

     d                                     dim(2)


     d thd_parm_ds_t   ds                  qualified

     d   tls_size_ind                 1a

     d   tls_key                     10i 0


     d thd_parm        ds                  likeds(thd_parm_ds_t)

     d                                     dim(2)

     d tls_key         s             10i 0

     d rtn             s             10i 0

     d status          s               *

     d tls_rls_proc    s               *   procptr




           // create TLS key (1)

           tls_rls_proc = %paddr(tls_destructor);

           rtn = pthread_key_create(tls_key : tls_rls_proc);

           if rtn <> 0;

               // error handling



           // launch child threads (2)

           thd_parm(1).tls_key      = tls_key;

           thd_parm(1).tls_size_ind = 'S'; // small TLS allocation

           thd_parm(2).tls_key      = tls_key;

           thd_parm(2).tls_size_ind = 'L'; // large TLS allocation


           rtn = pthread_create(%addr(thd(1))

                               : *null

                               : %paddr(thread_main)

                               : %addr(thd_parm(1)) );

           if rtn <> 0;

               // error handling


           rtn = pthread_create(%addr(thd(2))

                               : *null

                               : %paddr(thread_main)

                               : %addr(thd_parm(2)) );

           if rtn <> 0;

               // error handling



           // wait for child threads

           pthread_join(thd(1) : status);

           pthread_join(thd(2) : status);


           rtn = pthread_key_delete(tls_key);


           *inlr = *on;



     p thread_main     b

     d thread_main     pi              *

     d     param                       *   value


     d thd_parm        ds                  likeds(thd_parm_ds_t) based(param)

     d tls             s               *

     d SMALL_TLS_ALC   c                   32

     d LARGE_TLS_ALC   c                   33554432                             32MB




           // allocate TLS (3)


           if thd_parm.tls_size_ind = 'S';

               tls = %alloc(SMALL_TLS_ALC);

           elseif thd_parm.tls_size_ind = 'L';

               tls = ts_malloc(LARGE_TLS_ALC);


               // error handling





           // set TLS (4)

           pthread_setspecific(thd_parm.tls_key : tls);


           // call func() which consumes allocated TLS (5)



           return *null;


     p thread_main     e


     p tls_destructor  b

     d tls_destructor  pi

     d     tls                         *   value


     d rtn             s             10i 0



           // is TLS storage a teraspace allocation (6)

           rtn = testptr(tls : x'01');


           if rtn = 0;  // TLS storage is allocated from the default heap (7)

               dealloc tls;

           else;        // TLS storage is allocated from Teraspace (7)





     p tls_destructor  e


     /* procedure that consumes allocated TLS storage */

     p func            b

     d func            pi

     d     tls_key                   10i 0 value


     d cvthc           pr                  extproc('cvthc')

     d     receiver                    *   value

     d     source                      *   value

     d     length                    10i 0 value


     d tid             s              8a

     d chtid           s             16a

     d tls             s               *

     d greeting        s             32a   based(tls)




           // get TLS storage

           tls = pthread_getspecific(tls_key);


           // consume TLS storage

           tid = retthid();

           cvthc( %addr(chtid) : %addr(tid) : 16);

           greeting = 'From ' + chtid + ': Ni hao!';

           dsply 'Greetings' '' greeting;



     p func            e


     /* EOF */


Code notes:

(1) Call pthread_key_create () to create a thread local storage (TLS) key, passing the procedure pointer to the TLS destructor procedure tls_destructor().

(2) Launch child threads, passing the returned TLS key.

(3) Allocate TLS in a specific thread. During the storage allocation process, Pthread APIs pthread_lock_global_np() and pthread_unlock_global_np() are used to synchronize possible race conditions on job-level storage resources, such as activation group-based heap storage or a job's teraspace storage.

(4) Call pthread_setspecific() to make the allocated TLS storage visible to all procedures within the current thread.

(5) Consume allocated TLS storage in other procedures of the current thread.

(6) Once a thread exits, the registered TLS storage destructor procedure will be called to release corresponding TLS storage used by the thread. This example uses MI instruction TESTPTR to test whether the allocated TLS is allocated from a job's teraspace storage or not.

(7) Release allocated TLS storage in the TLS storage destructor.

Methods of Dynamic Program Storage Management

I have discussed different methods of dynamic program storage management. Which one you should choose depends on your particular case. For example, if a small piece of procedure-local dynamic storage is needed when implementing a utility service program that would be invoked in multi-threaded environments, dynamically allocated automatic storage would be preferred over other types of dynamic storage.


There are even more methods to make use of dynamic allocated storage on i5/OS—for example, using temporary or permanent space objects or using shared memory APIs declared in <sys/shm.h>. The reason I decided to introduce the above-mentioned methods is that these methods are more convenient and suitable for ILE HLLs such as ILE RPG.


Appendix A: Changing a Caller's Automatic Variables

As mentioned, a user program can address the user-domain automatic storage stack of the current thread by a space pointer. So it is not surprising that one procedure can address its caller procedure's or caller program's automatic storage after obtaining a space pointer to the automatic storage stack of the current thread.


Here are two ILE RPG programs that have been written just for fun. Program T040A calls program T040 from one of its subprocedures, func(), which has automatic data structure tom_sawyer. T040 obtains a space pointer to the current thread's automatic storage by MI instruction Materialize Invocation Attributes (MATINVAT) and then searches for caller program T040A's automatic data structure tom_sawyer and changes its content.


Caller Program T040A



      * @file t040a.rpgle


      * call program T040



     h dftactgrp(*no)


     d func            pr




           *inlr = *on;



     p func            b

     d func            pi


     d tom_sawyer      ds

     d     fb                         4a   inz('Tom')

     d     fc                         8a   inz('Sawyer')


     c                   call      'O45'

     c     'tom_sawyer'  dsply                   tom_sawyer

     p func            e


Program T040 Called by T040A 



      * @file t040.rpgle


      * @attention Program T040 makes changes to its caller program's

      *            automatic storage. It should be called by T040A.



      /copy mih52


     d asf_tmpl        ds                  likeds(matinvat_asf_selection_t)

     d                                     based(asf_tmpl_ptr)

     d asf_tmpl_ptr    s               *

     d asf_rcv         ds                  likeds(matinvat_asf_receiver_t)

     d asf_rcv_ptr     s               *   inz(%addr(asf_rcv))


     d spp_info_ptr    s               *

     d spcptr_info     ds                  likeds(matptr_spcptr_info_t)

     d                                     based(spp_info_ptr)

     d ptr             s               *

     d buf             ds         32767    based(ptr)

     d str12           ds            12    based(ptr)

     d pos             s             10i 0




           // init asf_tmpl

           asf_tmpl_ptr = modasa(matinvat_asf_selection_length);


                 : x'00'

                 : matinvat_asf_selection_length);


           asf_tmpl.num_attr   = 1;

           asf_tmpl.flag1      = x'00';

           asf_tmpl.ind_offset = 0;

           asf_tmpl.ind_length = 0;

           asf_tmpl.attr_id    = 2;

           asf_tmpl.flag2      = x'00';

           asf_tmpl.rcv_offset = 0;

           asf_tmpl.rcv_length = 16;


           // locate space pointer to ASF

           matinvat(asf_rcv_ptr : asf_tmpl_ptr);


           // retrieve current offset into ASF SPCPTR

           spp_info_ptr = modasa(matptr_spcptr_info_length);

           spcptr_info.bytes_in = matptr_spcptr_info_length;

           matptr(spp_info_ptr : asf_rcv.asf_ptr);


           // offset to the start of ASF SPCPTR

           ptr = asf_rcv.asf_ptr;

           ptr -= spcptr_info.offset;


           // find 'Tom Sawyer' and change it

           pos = %scan('Tom Sawyer' : buf);

           if pos <> 0;  // modify caller program's automatic storage

               ptr += pos - 1;

               str12 = 'Mark Twain';



           *inlr = *on;



Appendix B: Links and References

Fortress Rochester: The Inside Story of the IBM iSeries by Frank G. Soltis, ISBN-13: 978-1583040836


i5/OS V6R1 Programming Languages and programming toolkits


ILE RPG headers provided by the open-source project i5/OS Programmer's Toolkit

For the latest versions of these ILE RPG header files, refer to or directly check them out by svn from




Junlei Li

Junlei Li is a programmer from Tianjin, China, with 10 years of experience in software design and programming. Junlei Li began programming under i5/OS (formerly known as AS/400, iSeries) in late 2005. He is familiar with most programming languages available on i5/OS—from special-purpose languages such as OPM/ILE RPG to CL to general-purpose languages such as C, C++, Java; from strong-typed languages to script languages such as QShell and REXX. One of his favorite programming languages on i5/OS is machine interface (MI) instructions, through which one can discover some of the internal behaviors of i5/OS and some of the highlights of i5/OS in terms of operating system design.


Junlei Li's Web site is, where his open-source project i5/OS Programmer's Toolkit ( is documented.