I retired all those tired old techniques nearly 10 years ago. Today, I let the operating system do the work. Buried inside the iSeries 400 is a machine interface (MI) that performs the work for high-level languages (HLLs). These so-called MI instructions may be called by RPG IV or C programs.
The first MI instruction set appeared on the System/38, where it was difficult to get at, but one smart developer figured out how to tap into it. That programmer was Ken Kelley, now president of Advanced Systems Concepts, Inc., the makers of ABSTRACT and SEQUEL. In the mid-1980s, Ken published his methods for patching the CL compiler to access MI. This opened up a new world of development that slowly made its way to the mainstream environment. Today, we don't need to know the secrets of patching compilers to get at the MI because IBM actually provides an MI compiler with OS/400.
To provide easier access to those very useful MI instructions, IBM created a set of wrapper functions written for the C compiler. This means that you can actually write MI instructions using C on the iSeries or AS/400. For example, in MI there is a cvthc (convert character to hex) instruction that does in one instruction what it used to take countless lines of hand-written RPG code to do.
Okay, you're an RPG programmer. So who cares about C anyway? The answer is, you do--because C is a powerful language on the AS/400. You can access the entire C runtime library and the MI instruction "library" from RPG IV. The OS/400 C compiler contains the traditional C language functions, such as memset(), sprintf(), time(), and so forth, which are part of the C runtime library.
So how do you get at the C runtime library, and consequently the MI instructions, without learning the C language? That's the easy part; it's called program binding. Program binding gives RPG IV the ability to call procedures written in any programming language. To use any MI instruction or C language runtime function in RPG IV, you need to prototype it and link it to the C runtime library, using a binding directory. In the C language, the entire runtime library of functions is actually a set of procedures that are callable from any HLL that supports binding, including good old RPG IV. This means that just about any function in the C language can be called from within RPG IV, including the MI instruction set.
So is RPG IV more powerful than C? Well, RPG IV can call virtually every runtime function of C, and C can call procedures written in RPG IV, but C cannot call any RPG IV operation codes or built-in functions.
Let me show you how to use the C runtime library functions in your own RPG IV code. Two MI instructions and two C functions immediately come to mind as being useful. The MI instructions provide a simple way to convert from and to hex. The C functions do two things: one allows you to move a character repeatedly to a target memory location; the other converts character values into numeric.
The two MI instructions that perform conversion from hexadecimal to character and back again are cvthc and cvtch. I've seen dozens of routines that convert the character string 'C1' into the letter 'A' and back again. Most involve a subroutine that uses math and shifting of bits. All these routines became obsolete with the introduction of these two MI instructions.
- cvthc--Converts from a character value to the hexadecimal text of the character values (e.g., from 'A' to X'C1')
- cvtch--Converts from hexadecimal text to the character form of the hex values. (e.g., from X'C1' to 'A')
The interesting thing about these instructions is that they support conversion of single or multiple characters to and from hexadecimal. So you need only call these routines once per field rather than once per character.
Figure 1 shows the prototype for both MI instructions. To use them, simply store the prototypes in a separate source member and use /COPY or the new /INCLUDE directive to include them in your program.
The hexadecimal representation of the value appears as pairs of characters. Consequently, the hexadecimal parameter must be at least twice the length of the character field.
|
Figure 1: Prototypes for cvthc and cvtch MI Instructions
Note that on line 1 in Figure 1, the BNDDIR('QC2LE') keyword is specified. This is required when your program is taking advantage of any C runtime library functions. Do not include that line in the /COPY member, but rather in the source member being compiled. QC2LE is an IBM-provided binding directory that includes the names of the service programs the C runtime library uses. By using that binding directory, the RPG compiler binds to the C runtime functions. No additional programming required!
Other interesting C runtime functions can be found in the QCLE library (Version 4) or the QCPPLE library (Version 5 and later). The QACSRC source file contains examples of how to use all MI and appropriate C runtime functions. These examples, however, are written in the C language.
The C language's memset() function is an interesting utility. Memset() copies a single character repeatedly to a target memory location (e.g., a field). While this is similar to the MOVE *ALL'c' capability of RPG, memset() can be used to copy data to dynamically allocated memory locations. So if you don't know the size until runtime, memset() is a good choice.
|
Figure 2 : Prototype for the Memset() Function
Figure 2 shows the prototype source code for the memset() function. Note that each parameter is passed by value. By default, C passes parameters by value, whereas traditional OS/400 passes parameters by reference. To ensure that your code is compatible with the C language, use the VALUE keyword on the prototype.
Also note the EXTPROC keyword (line 1). This keyword identifies or names the function being prototyped. You need it when you're prototyping procedures or functions written in languages that support mixed names, such as C. That is, MEMSET is not the same as memset to a C program. So in RPG, we prototype it and enclose its real name (in lowercase) in single quotes: 'memset'. In RPG calculation specifications, you can use memset, Memset, MemSet, or MEMSET; it all reverts back to the 'memset' function.
The first parameter of memset() is the address of the memory that will receive the repeated character. Normally, you would specify either a pointer variable that contains the address of allocated memory or %addr(f) where 'f' is a field in your program. The %addr() built-in function returns the address of the field being addressed. Addresses are stored in pointer variables.
|
Figure 3: Use Memset to Initialize Dynamic Storage
In Figure 3, the field pValue is a pointer whose value is set on line 7. The ALLOC operation code allocates the number of bytes of memory specified in Factor 2. The field nSize is used on line 7 to store the number of bytes to be allowed. The built-in function %size(Name) returns the value 30, which is then multiplied by 50. So 1500 bytes of memory are allocated on line 8. The address of that memory is returned and stored in the pValue field.
Then, on line 9, hex 40's are moved into the dynamically allocated storage. I could have used a simple ' ' (a quoted blank space) and gotten the same result, but I wanted to show you how to use hex values, since using hex values with memset() is much more common.
So, once line 9 is performed, 1500 bytes of new memory are allocated and set to blanks. For more information on the ALLOC or the REALLOC operation codes, see The Modern RPG IV Language. Next time, I'll show you how to use this technique to
dynamically allocate array elements at runtime. So remember to tell your
friends!
LATEST COMMENTS
MC Press Online