23
Sat, Nov
1 New Articles

Can an ILE CL Program Know the Number of Parameters Passed to It?

CL
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Investigate the ILE CL compiler's mechanism for receiving program parameters.

 

There's no doubt that the program-call mechanism in IBM i is fully dynamic. The called program can be determined at run time instead of at compile time. The parameter list passed to the called program can be composed at run time by the calling program. Also, the length of the parameter list for a called program can be changed dynamically. As you might know, an ILE RPG program can retrieve of the length of the parameter list via the RPG %PARMS built-in function (BIF). An ILE C program can also achieve this goal via the first argument of its main function, int argc. So is it possible for an ILE CL program to know the number of parameters passed to it? This article will lead you to answer, all the way down to the RISC instruction level.

 

Two fields in the program header of an MI program object determine the Number of parameters attribute of the program: PEP MIN PARMS and PEP MAX PARMS, meaning the minimum and maximum number of parameters that can be passed to the program entry procedure (PEP) of program. Upon a program-call (due to a Call External (CALLX), a Transfer Control (XCTL), or a Call Program with Variable Length Argument List (CALLPGMV) MI instruction), if the length of the parameter list passed to the called program is out of the range from PEP MIN PARMS to PEP MAX PARMS of the called program, a hex 0802 (Argument List Length Violation) exception will be signaled. Different compilers choose different policies in determining the range of the allowable parameter list length. In OMI, the programmer is responsible for specifying the minimum and maximum number of parameters of an OMI program. Additionally, the calling program can set the length of the parameter list to a called program via the Set Argument List Length (SETALLEN) OMI instruction, and the called program can retrieve the number of parameters passed to it via the Store Parameter List Length (STPLLEN) OMI instruction. Experiments show that for an OPM RPG or an OPM COBOL program, the minimum number of parameters is set to zero, and the maximum number of parameters is set to the length of the program-defined parameter list. For an ILE program, the length of the parameter list is determined by the entry point procedure of the PEP module of the program. For example, the parameter list length of an ILE COBOL module ranges from zero to the number of parameters of the program-defined parameter list, and that of an ILE CL module ranges from zero to 255. Therefore, a calling program is able to call an ILE CL program (or more exactly, an ILE program the PEP module of which is an ILE CL module) and pass up to 255 parameters even though the called program does not expect any parameter. This makes it more important for an ILE CL program to know the exact number of parameters passed to it.

The OPM Parameter Count (_OPM_PARM_CNT) System Built-In

According to the documentation of the_OPM_PARM_CNT system built-in in the IBM i Information Center, the parameter count in the non-bound program parameter list received by the program entry procedure is returned by _OPM_PARM_CNT. So can we retrieve the parameter list length via this system built-in?

 

However, the same documentation shows that a user program written in any ILE High-Level Language(s) (HLL) cannot utilize the _OPM_PARM_CNT system built-in: This function can only be used by procedures defined to be a program entry procedure. Otherwise, an instruction stream not valid (hex 2A1B) exception will be signaled during module creation. As you might know, ILE compilers generate an entry point procedure into each compiled module object. (A DSPMOD DETAIL(*PROCLIST) command will show you that.) Only the entry point procedure of a module can act as the PEP of an ILE program; therefore, a user procedure in a compiled ILE program never has the chance to become the PEP of the program.

The NPM Procedure Parameter List Address (_NPMPARMLISTADDR) System Built-In

The documentation of the_NPMPARMLISTADDR system built-in in the IBM i Information Center described this system built-in as follows:

The address of the New Program Model parameter list received by the current invocation is returned. ... Arguments is a variable length field (at offset hex 20) used to pass argument values to the current invocation.

 

Can we use the _NPMPARMLISTADDR system built-in in the main procedure of an ILE CL program to retrieve the number of parameters passed? The following is a tiny CL program, parm05.clle, that displays the first 96 bytes of the arguments field in the parameter area returned by _NPMPARMLISTADDR.

 

PGM       PARM(&A &B &C)

DCL       VAR(&A) TYPE(*CHAR) LEN(1)

DCL       VAR(&B) TYPE(*CHAR) LEN(1)

DCL       VAR(&C) TYPE(*CHAR) LEN(1)

DCL       VAR(&PRMARA@) TYPE(*PTR)

DCL       VAR(&CNT) TYPE(*INT) LEN(4) VALUE(0)

DCL       VAR(&PRMCNT) TYPE(*CHAR) STG(*DEFINED) +

           LEN(4) DEFVAR(&CNT)

DCL       VAR(&ARG@) TYPE(*PTR)

DCL       VAR(&PRMARA) TYPE(*CHAR) STG(*BASED) +

           LEN(128) BASPTR(&PRMARA@)

DCL       VAR(&ARG) TYPE(*CHAR) STG(*BASED) LEN(16) +

            ASPTR(&ARG@)

CALLPRC   PRC('_NPMPARMLISTADDR') RTNVAL(&PRMARA@)

CHGVAR     VAR(&ARG@) VALUE(%ADDR(&PRMARA))

CHGVAR     VAR(%OFS(&ARG@)) VALUE(%OFS(&PRMARA@) + 32)

DOWHILE   COND(%OFS(&ARG@) *LT (%OFS(&PRMARA@) + 128))

  SNDPGMMSG MSGID(ART0102) MSGF(ARTMSG) MSGDTA(&ARG)

CHGVAR     VAR(%OFS(&ARG@)) VALUE(%OFS(&ARG@) + 16)

ENDDO

SNDPGMMSG MSGID(ART0101) MSGF(ARTMSG) MSGDTA(&PRMCNT)

ENDPGM

 

Note that the program-defined parameter list of PARM05 takes three parameters. Note also that the &CNT and &PRMCNT variables and the SNDPGMMSG MSGID(ART0101) MSGF(ARTMSG) MSGDTA(&PRMCNT) command are for the experiment at the end of this article.

 

Prepare a message file that is needed by PARM05 like this:

 

CRTMSGF MSGF(ARTMSG)

ADDMSGD MSGID(ART0101) MSGF(ARTMSG) MSG('Totally &1 parameters passed.') FMT((*UBIN 4))

ADDMSGD MSGID(ART0102) MSGF(ARTMSG) MSG('&1') FMT((*HEX 16))

 

Call program PARM05 with three parameters. The content of the arguments field returned by _NPMPARMLISTADDR might look like the following:

 

4 > call parm05 (a b c)                  

   X'8000000000000000FC19EC7E44001646'

   X'8000000000000000FC19EC7E44001667'

   X'8000000000000000FC19EC7E44001688'

   X'00000000000000000000000000000000'

   X'0100000000000060151081087FFF8020'

   X'0000000000000224000000000F000000'

 

The content of the arguments field contains three space pointers addressing the parameters passed to PARM05. It shows that _NPMPARMLISTADDR is able to return all the program-defined parameters.

 

Call program PARM05 again with five parameters. The content of the arguments field might look like the following:

 

4 > call parm05 (a b c d e)

   X'8000000000000000FC19EC7E44001646'

   X'8000000000000000FC19EC7E44001667'

   X'8000000000000000FC19EC7E44001688'

   X'00000000000000000000000000000000'

   X'0100000000000060151081087FFF8020'

   X'0000000000000224000000000F000000'

 

The fourth and fifth parameters passed to PARM05 are not returned by _NPMPARMLISTADDR. Does this mean that the _NPMPARMLISTADDR system built-in cannot be used to retrieve the parameters passed to the main procedure of an ILE CL program? Or the main procedure (user procedure) of an ILE CL program does not receive the full parameter list passed by the calling program? This question will be answered later in this article by investigating the actual PEP of an ILE CL program, _CL_PEP.

 

Finally, call program PARM05 with two parameters. The content of the arguments field might look like the following:

 

4 > call parm05 (a b)

     X'8000000000000000FC19EC7E44001646'

     X'8000000000000000FC19EC7E44001667'

     X'00000000000000000000000000000000'

     X'00000000000000000000000000000000'

     X'0100000000000060151081087FFF8020'

     X'0000000000000224000000000F000000'

 

The content of the arguments field contains two space pointers addressing the parameters passed to PARM05, followed by 16-byte hex 00. The following information is supplied in the Passing parameters section of the CL Programming book, which is available in the IBM i Information Center:

 

When calling an ILE program or procedure, the operating system does not check the number of parameters that are passed on the call. In addition, the space where the operating system stores the parameters is not reinitialized between program or procedure calls. Calling a program or procedure that expects n parameters with n-1 parameters makes the system use whatever is in the parameter space to access the nth parameter. The results of this action are very unpredictable. This also applies to programs or procedures written in other ILE languages that call CL programs or procedures or are called by CL programs or procedures.

 

According to this documentation, the 16 bytes at offset hex 20 in the content of the arguments field should be the third space pointer (X'8000000000000000FC19EC7E44001688') shown in the previous call to PARM05 instead of 16-byte hex 00. Obviously, the result of our experiment doesn't coincide with the above-shown documentation. This will be explained later via our investigation into the _CL_PEP procedure.

Investigate the _CL_PEP Procedure

_CL_PEP is the entry point procedure generated by the ILE CL compiler for each CL module. When a CL module is chosen as the PEP module of an ILE program (due to a CRTPGM command or a CRTBNDCL command), the _CL_PEP procedure of the CL module becomes the program entry procedure (PEP) of the program. The following is the PowerPC instruction stream of the _CL_PEP procedure (at VRM540) of the above-mentioned ILE CL program PARM05. The _CL_PEP operates on the parameter list passed to the program and then passes control to the user procedure PARM05. The operations on the parameter area are explained by pseudocode and comments.

 

RISC INSTRUCTIONS (_CL_PEP)

LOCATION   OBJECT TEXT       SOURCE STATEMENT         Comments/Pseudocode

... ...

   000040    607E0000         ORI 30,3,0               [1]

... ...

   000094   3B000000         ADDI 24,0,0

   000098   931FFF40         STW 24,0XFF40(31)       index = 0

   00009C   837E0004         LWZ 27,0X4(30)           [2]

   0000A0   937FFF44        STW 27,0XFF44(31)       num_parms_passed = number of parameter passed

   0000A4   833FFF40         LWZ 25,0XFF40(31)       loop: // [3]

   0000A8   3B590001         ADDI 26,25,1             index += 1

   0000AC   7B5A0020         RLDICL 26,26,0,32       Clear the higher 32 bits of index

   0000B0   935FFF40         STW 26,0XFF40(31)       Save the increased value of index back to (automatic) program storage

   0000B4   299A0003         CMPLI 3,0,26,3

   0000B8   418D0090         BC 12,13,0X90           if index > num_parms_defined; then goto end_loop // [4]

   0000BC   831FFF40         LWZ 24,0XFF40(31)

   0000C0   837FFF44         LWZ 27,0XFF44(31)

   0000C4   7E18D840         CMPL 4,0,24,27

   0000C8   41910050         BC 12,17,0X50           if index > num_parms_passed; then goto not_passed_parm

   0000CC   833FFF40         LWZ 25,0XFF40(31)

   0000D0   3B59FFFF         ADDI 26,25,-1

   0000D4   7B5A0020         RLDICL 26,26,0,32

   0000D8   7B5B26E4         RLDICR 27,26,4,59

   0000DC   3B1FFF50         ADDI 24,31,-176         parm1_ptr_addr = r31 - 0xB0 // [5]

   0000E0   7F38DA14         ADD 25,24,27             parm_ptr_addr = parm1_ptr_addr + (16 * (index - 1)) // [6]

   0000E4   7FD9C088         TD 30,25,24

0000E8   835FFF40         LWZ 26,0XFF40(31)

   0000EC   837E0004         LWZ 27,0X4(30)

   0000F0   7D3AD840         CMPL 2,1,26,27

   0000F4   40E9000C         BC 7,9,0XC               if index <= num_parms_passed; then goto copy_parm_ptr

   0000F8   EB008138         LD 24,0X8138(0)         Control will never be branched to here

   0000FC   4800000C         B 0XC

   000100   7B5B1F24         RLDICR 27,26,3,60       copy_parm_ptr:

   000104   7F1ED82A         LDX 24,30,27             addr=parm_area_addr + index * 8 // [7]

   000108   E358000F         LQ 26,0X0(24),15

   00010C   FB590002         STQ 26,0X0(25)           parm_ptr = *(addr) // [8]

   000110   41D58023         BCLA 14,21,0X8020

   000114   4BFFFF90         B 0X3FFFF90             goto loop // Handle the next parameter passed to the program

   000118   831FFF40         LWZ 24,0XFF40(31)       not_passed_parm:

   00011C   3B78FFFF         ADDI 27,24,-1

   000120   7B7B0020         RLDICL 27,27,0,32

   000124   7B7A26E4         RLDICR 26,27,4,59

   000128   3B3FFF50         ADDI 25,31,-176         parm1_ptr_addr = r31 - 0xB0 // [5]

   00012C   7F19D214         ADD 24,25,26             parm_ptr_addr = parm1_ptr_addr + (sizeof(ptr) * (index - 1)) // [6]

   000130   7FD8C888         TD 30,24,25

   000134   3B600000         ADDI 27,0,0

   000138   FB780000         STD 27,0X0(24)

   00013C   FB780008         STD 27,0X8(24)           parm_ptr = hex 00000000000000000000000000000000 // [9]

   000140   41D58023         BCLA 14,21,0X8020

   000144   4BFFFF60         B 0X3FFFF60             goto loop

   000148   3B5FFF50         ADDI 26,31,-176         end_loop:

   00014C   3B3FFFA0         ADDI 25,31,-96           [10]

   000150   E2DA000F         LQ 22,0X0(26),15

   000154   FAD90022         STQ 22,0X20(25)

   000158   E2DA001F         LQ 22,0X10(26),15

   00015C   FAD90032         STQ 22,0X30(25)

   000160   E2DA002F         LQ 22,0X20(26),15

   000164   FAD90042         STQ 22,0X40(25)

000168   63230000         ORI 3,25,0               [11]

   00016C   63A20000         ORI 2,29,0

   000170   48000091         BL 0X90                 call-procedure PARM05 // [12]

... ...

 

Notes

[1] From Chapter 33: Analysis of SCV 7, Program Call of Leif Svalgaard's e-book AS/400 Machine Level Programming, we know that the address of the parameter area to a called program during a program call is passed via General Purpose Register (GPR) 3 (r3). Later, you'll find out that r3 is also used to pass the address of the parameter area during a procedure call. Here, the content of r3 is copied to r30.

[2] Load the number of parameters passed into r27 from the address of the parameter area (now stored in r30) at offset 4. By debugging an OMI program, you learn that during a program call the number of parameters and each individual parameter are stored in the parameter area for the called program as the following:

  • A BIN(4) field at offset 4 is the number of parameters passed.
  • 8-byte Single-Level Store (SLS) addresses of all parameters are stored in the parameter area sequentially starting from offset 8. By convention, parameters for a program call are passed by references, which means each 8-byte address in the parameter area addresses a space pointer to the corresponding parameter.

[3] Start of the parameter-handling loop.

[4] num_parms_defined is the number of the parameters in the parameter list defined by the program. In this example, the value of num_parms_defined is 3. As shown by the program logic, parameters whose index numbers are greater than the length of the program-defined parameter list are simply ignored by _CL_PEP. Therefore the _NPMPARMLISTADDR system built-in issued in the main procedure of an ILE CL program cannot retrieve any parameters other than the program-defined parameters.

[5] As you might know, r31 always addresses the upper limit of the automatic storage frame (ASF) of the current invocation. The ADDI 24,31,-176 instruction locates the address of the space pointer to the first program-defined parameter in the ASF of _CL_PEP.

[6] Here, parm_ptr_addr means the address of the space pointer to the current parameter in the ASF of _CL_PEP. parm_ptr_addr is computed by adding (index - 1) * 16 to the address of the space pointer to the first program-defined parameter. 16 is the size of an MI pointer.

[7] addr is set to the 8-byte SLS address at offset (8 * index) from the beginning of the parameter area.

[8] The space pointer addressing the index parameter is copied from address addr to the ASF location where the space pointer to the index program-defined parameter is stored.

[9] For each parameter that is not passed by the caller program, the space pointer addressing the corresponding parameter is set to 16-byte hex 00. Therefore, in the main procedure of an ILE CL program, you can test whether a specific parameter is passed by testing the address of the parameter via any method that can be used to test for a null pointerfor example, the Compare Pointer Type (CMPPTRT) MI instruction with the comparison value set to hex 00.

[10] Before _CL_PEP passes the control to the user procedure PARM05, the space pointers to the program-defined parameters recorded by _CL_PEP are copied to the parameter area for procedure PARM05, which can be retrieved via the _NPMPARMLISTADDR system built-in in procedure PARM05. In this example, (r31 - 0x60) is the address of the parameter area for procedure PARM05. PARM05 takes three program-defined parameters. The corresponding space pointers are copied from (r31 - 0xB0) to location (r31 - 0x60 + 0x20).

[11] r3 is set to the address of the parameter area (r31 - 0x60) for procedure PARM05.

[12] Finally, call procedure PARM05.

The Final Experiment: Display the Number of Parameters Passed to an ILE CL Program

From the previous analysis of the _CL_PEP procedure of an ILE CL program, we know that the compiler-generated procedure knows the number of parameters passed to the program very well; it just doesn't pass this information to the user procedure of the CL program. In the following experiment, let's make a tiny modification to the RISC instruction stream of the user procedure of ILE CL program PARM05 to retrieve the number of parameters passed to the program, which was omitted by the _CL_PEP procedure.

 

Observe the RISC instructions generated for _CL_PEP again. You'll find that the content of r30 (which is set to the value of r3 at the beginning of _CL_PEP) remains unchanged until procedure PARM05 is invoked. Therefore, we may have the chance to obtain the address of the parameter area of the program stored in r30. Look at the start of the RISC instructions of the PARM05 procedure:

 

RISC INSTRUCTIONS (PARM05)

LOCATION   OBJECT TEXT       SOURCE STATEMENT         Comments

... ...

   000040   607E0000         ORI 30,3,0               Copy r3 to r30

   000044   3B600000         ADDI 27,0,0

   000048   937FFD74         STW 27,0XFD74(31)       Initialize CL variable &CNT to zero

 

Change the two PowerPC instructions at offset hex 40:

 

LOCATION   OBJECT TEXT       SOURCE STATEMENT         Comments

   000040   837E0004         LWZ 27,0X4(30)           Load the number of parameters passed to the program from r30 at offset 4

   000044   607E0000         ORI 30,3,0               The original instruction at offset hex 40

   000048   937FFD74         STW 27,0XFD74(31)       Store the number of parameters passed to the program into CL variable &CNT

 

Now, call the modified PARM05 program with different numbers of parameters to test the modification. For example, CALL PARM05 (A B C D E), the output might look like the following:

 

4 > CALL PARM05 (A B C D E)

     X'8000000000000000FC19EC7E44001646'

     X'8000000000000000FC19EC7E44001667'

     X'8000000000000000FC19EC7E44001688'

     X'00000000000000000000000000000000'

     X'01000000000000600000000000000000'

     X'0000000000000224000000000F000000'

     Totally 5 parameters passed.

 

Yes, we've managed to get the number of parameters passed to our ILE CL program! However, it is only an experiment rather than a practical solution. What we actually need is a built-in support for retrieving the length of the parameter list passed to a CL PEP. What about a %PARMS built-in in future improvements to CL? Maybe, and you've already seen how easy it is to implement such a built-in for CL.

 

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 http://i5toolkit.sourceforge.net/, where his open-source project i5/OS Programmer's Toolkit (https://sourceforge.net/projects/i5toolkit/) is documented.

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: