1 H 2 H* 3 H* CREATE ISAM STUDENT MASTER FILE FROM CARDS 4 H* 5 FINCARDS IPE F 80 80 READ40 6 FSTMASTR O F 250 250 7KI 2 DISK14 S 7 FREPORT O F 132 132 OF LPRINTER 8 LREPORT 101 6012 9 IINCARDS AA 01 1 CA 10 I 3 9 IDENTA 11 I 10 44 NAME 12 I 45 79 ADDR1 13 I BB 02 1 CB 14 I 3 9 IDENTB 15 I 10 44 ADDR2 16 I 45 45 GENDER 17 I 46 550PHONE 18 I 56 630BDATE 19 I 64 66 MAJOR 20 C* 21 C* DURING FIRST CYCLE (1P), SETUP CONSTANT FIELDS 22 C* TO INITIALIZE MASTER FIELDS NOT PROVIDED ON 23 C* INPUT CARDS 24 C* 25 C 1P Z-ADD0 Z5 52 26 C 1P MOVE ' ' B40 40 27 C* 28 C* COUNT INPUT CARDS OF EACH TYPE FOR REPORT TOTALS 29 C* 30 C 01 CACNT ADD 1 CACNT 30 31 C 02 CBCNT ADD 1 CBCNT 30 32 C* 33 C* FOR "A" CARD, IF LAST CARD READ NOT "A" CARD, 34 C* SETON INDICATOR 20 35 C* 36 C 01 SETOF 20 37 C 01 ' ' COMP PREVA 20 38 C* 39 C* IF 2 CONSECUTIVE "A" CARDS READ, SAVE STUDENT ID 40 C* OF SET MISSING THE "B" CARD FOR REPORTING 41 C* 42 C 01N20 MOVE PREVA MISSB 7 43 C* 44 C* 45 C* FOR "A" CARD, SAVE STUDENT ID IN PREVA 46 C* 47 C 01 MOVE IDENTA PREVA 7 48 C* 49 C* FOR "B" CARD, IF MATCHES PREVIOUS "A" CARD, 50 C* SETON INDICATOR 21 51 C* 52 C 02 SETOF 2021 53 C 02 IDENTA COMP PREVA 21 54 C* 55 C* IF THIS "B" MATCHES PREVIOUS "A" AND THE STUDENT 56 C* ID IS GREATER THAN LAST STUDENT ID ADDED, 57 C* SETON INDICATOR 20 58 C* 59 C 02 21 IDENTB COMP KEYH 20 60 C 02 20 MOVE IDENTB KEYH 7 61 C* 62 C* CARD SET IS COMPLETE, COUNT AS ADDED RECORD 63 C* 64 C 02 20 ADDCNT ADD 1 ADDCNT 30 65 C* 66 C* WHEN CARD "B" IS READ, RESET ID FROM PREVIOUS CARD "A" 67 C* 68 C 02 MOVE ' ' PREVA 69 OSTMASTR D 02 20 70 O B40 250 71 O Z5 210P 72 O Z5 207P 73 O 204 '000000' 74 O Z5 198P 75 O 195 '000000' 76 O Z5 189P 77 O 186 '000000' 78 O Z5 180P 79 O 177 '000000' 80 O Z5 171P 81 O 168 '000000' 82 O Z5 162P 83 O 159 '000000' 84 O Z5 153P 85 O 150 '000000' 86 O Z5 144P 87 O 141 '000000' 88 O MAJOR 135 89 O BDATE 132 90 O PHONE 124 91 O GENDER 114 92 O ADDR2 113 93 O ADDR1 78 94 O NAME 43 95 O IDENTB 8 96 O 1 'A' 97 OREPORT H 201 1P 98 O OR OF 99 O 23 'CREATION' 100 O 15 'STUDENT MASTER ' 101 O D 1 02 20 102 O NAME 52 103 O 17 'ADDED: ' 104 O IDENTB 9 105 O D 1 02N20 106 O 51 'B CARD WITHOUT A CARD' 107 O IDENTB 9 108 O D 1 01N20 109 O 51 'A CARD WITHOUT B CARD' 110 O MISSB 9 111 O T 31 LR 112 O 20 '"A" CARDS READ' 113 O CACNT Z 5 114 O T 1 LR 115 O 20 '"B" CARDS READ' 116 O CBCNT Z 5 117 O T 1 LR 118 O 19 'RECORDS ADDED' 119 O ADDCNTZ 5 120 O T 31 LR 121 O 22 '*** END OF REPORT ***' |
Statements 5-7 are the File Description Specifications for the card input (INCARDS), the ISAM output (STMASTR), and a report output (REPORT) files. Note that the ISAM file will be unblocked because the block size (columns 20-23) and the record size (columns 24-27) both contain 250. The only performance gain in blocking ISAM records is for sequential access, and considering the scale at which we are operating under Hercules/MVT, the additional overhead is not worth the bother. These are the entries that are specific for the ISAM file to be created -
The device for the ISAM file (columns 40-46) is DISK14, which indicates a disk of type 2314. The file may actually reside on a 3330 (as it does on my system), but DISK14 is what you need to use for any file contained on a disk device to keep the RPG I compiler happy. No other entries on the file specifications should be new if you have studied and understood the previous example programs.
Statements 9 and 13 define the two types of records that are expected to occur in the input file INCARDS. Unlike the previous example program, I have decided to omit the sequence and number information and do some manual checking in the calculation specifications, so the sequence field (columns 15-16) for these two statements contain AA and BB respectively. This specifies there is no particular sequence to be checked as the cards are read. Record indicators 01 and 02 are specified to be turned on when the corresponding record type has been read by the program. To identify the input data to the input specification, columns 21-27 specify that card column one will contain either an A or B to differentiate each input record read from the two possibilities.
Statements 10-12 and 14-19 define the input fields associated with each record type. Unlike the previous example program, the student identification number will be read into a unique field name for each record, even though the number should be the same in both records for an individual student. The reason for this will become obvious when you look at the calculation specifications.
Although I have been generous with comments in the calculation specifications, I will elaborate further on what has been done here since thoroughly understanding the calculations is key to understanding the output generated by the program.
In addition the the data read in from the two card formats, the created student record will reserve space for additional fields which will be updated by a subsequent program, as well as some unused space for future expansion. Although it is not essential to initialize unused alphanumeric fields to spaces when creating a new file, I usually do so that what was written is clear when the program is examined later. Statements 25-26 initialize a couple of "constant" fields containing 5 zeros and 40 spaces respectively. These fields will be written into the output record as "placeholders" for data to be added later. These statements are conditioned with the first page (1P) indicator which is on at the beginning of the program and remains on until the first detail output cycle is complete. Therefore, these two statements will only execute one time, during the first cycle of the program.
Statements 30-31 are used to count the number of each type of input card read. Each statement adds 1 to the corresponding counter, which is defined by the entries in field length (columns 49-51) and decimal positions (column 52). Each statement is conditioned with the indicator associated with the corresponding input record type - indicator 01 for cards containing a "A" in column one, and indicator 02 for cards containing a "B" in column one.
Statements 36-37 test for the presence of an "A" card without a corresponding "B" card (the test is literally checking for two consecutive "A" cards). Both statements are conditioned with indicator 01 which will be on only when a type "A" card has been read. First, indicator 20 is set off. Then, a field which will contain the student identification number from the previous "A" card, if it was not matched by a "B" card, is tested to see if it contains spaces. If the comparison is equal, indicator 20 will be set on (column 58).
Statement 42 stores the student identification number from the previously read "A" card which did not have a matching "B" card. This allows me to print the number on the report with an error message.
Statement 47 moves the contents of the student identification number from the type "A" card into a work field. This statement is also conditioned by indicator 01, which is only on when a type "A" card has been read.
Statements 52-53 ensure that the "B" card matches the previously read "A" card. These two statements are conditioned with indicator 02 which will be on only when a type "B" card has been read. First, indicators 20 and 21 are set off. Then the student identification number from the "B" card is compared to the student identification number from the previously read "A" card. If they compare equal, indicator 21 will be set on (column 58).
Looking ahead to the output point of this cycle and considering what we know right now, if indicator 02 and indicator 21 are on, we have read a "B" card containing a student identification number that matched the student identifcation number from the previously read "A" card. So, we have all of the information for a single student stored in the input fields from the two cards ready to be written to output.
But, there is one more test I wanted to make. Statements 59-60 ensure that the input cards have been presented to the program sorted into sequence on the student identification number. When an ISAM file is loaded in the manner we are loading it here, the records must be written out in key sequence. If an attempt is made to write a record out of sequence, H0 will be set on and the program will terminate abnormally. Statement 53 is conditioned with indicators 02 and 21 - remember they will be on if we have read a matching pair of "A" and "B" cards. Statement 53 compares the student identification number from the "B" card (the student identification number from the "A" card could also have been used) with a work field which contains the last student identification number added to the ISAM file. If the current student identification number is greater, indicator 20 is set on (column 54). At this point, if indicators 02 and 20 are on, we know that we have a complete set of cards and that they are in the proper sequence to write the new student record. So, Statement 54 is conditioned by indicators 02 and 20 and it moves the student identification number from the "B" card into the work field used to test the sequence of the input cards.
Statement 64 counts the records added to the ISAM file. Since we know that if indicators 02 and 20 are both on we will be able to add the record, we can also condition this add operation with those same indicators.
One last function is performed by Statement 68 when indicator 02 is on (indicating that a "B" card has been read). Since we want to know if there are two consecutive "A" cards, we reset the work field that holds the student identification number from the prior "A" card read to spaces. Again, this operation is conditioned by indicator 02, so it will only happen when a "B" record has been read.
You may notice that I used indicator 20 for tests when both the "A" card was read (indicator 01 on) and the "B" card was read (indicator 02 on). Because only indicator 01 or indicator 02, but not both, will be on during a single cycle, it is possible to reuse the indicator with impunity. Many beginning RPG programmers (and I include myself in this statement) tend to use many different indicators when only a few will do the job adequately. There are only 99 general purpose indicators and I have seen several tightly written RPG programs that came close to running out. If you haphazardly assign indicators, you can run out quickly.
If you are comfortable in understanding the calculation specifications, the output specifications that follow are quite simple.
Statement 69 defines the conditions under which the new student record will be written to the ISAM file (STMASTR). At detail output time, if indicators 02 ("B" card has just been read) and 20 (student identification number is greater than previously written student identification number), the record will be written. It may seem that I have omitted something - didn't indicator 21 need to be included to signal that both an "A" and "B" card had been read for this student identification number? Although I could have included it here to condition the output, the comparison that set 20 is conditioned by indicator 21, so there is no need to repeat the indicator here.
Statements 70-96 specify the data fields to write to the output ISAM file. Statement 70 uses the work field B40 to write 40 blanks at the end of the record, for future expansion. Statements 71-87 initialize fields which will later be updated to contain a grade point average and a table of courses and course grades for the student. Statements 88-95 write the fields read from the input cards. Statement 96 places an "A" in the first position of the record to indicate that this is an "active" record. The indexed sequential access method provides a facility for flagging deleted records by placing a hexadecimal value x'FF' in the first position for deleted records and a hexadecimal value x'00' for active records. If this method is used, the ISAM utility IEBISAM can be used to re-organize the file, removing deleted (inactive) records. I have never used the IEBISAM program, always preferring to write my own reorganization programs. Thus, I usually used "A" and "D" for this purpose. Also, I'm not sure I ever knew how to define a hexadecimal constant in RPG when I was using ISAM files (the last time was in 1980, just before we switched to VSAM), and I certainly don't remember how to now.
The remainder of the program, Statements 97-121 define the lines to print on the report and don't contain anything that should be unfamiliar if you have looked at the previous example programs.
The compiler listing and an IEBISAM PRINTL listing of the created ISAM dataset are available for viewing here: Example 3 SYSOUT.
The jobstream, complete with JCL, RPG source, input data, and an IEBISAM step to list the created ISAM dataset are available for download here: Example 3 jobstream. You will need to ensure that the volume serial number where the ISAM dataset is to be created is correct for your MVT system before running the jobstream. Note: There are two jobstreams in this archive, one is for MVS and the other is for MVT. The difference is that the MVS jobstream uses the ISAM-VSAM interface to access an VSAM cluster instead of creating a native ISAM dataset.
This program and the next (Example 4) re-acquainted me with a bug in the RPG compiler that I last dealt with in 1974. As I recall, we never fixed the bug then, we just worked around it. The bug is this: if you attempt to randomly retrieve records from an ISAM file loaded by an RPG program, when an attempt is made to read a record using a key that does not exist in the file, the system will hang, forcing the job to be cancelled by the operator. I got past this with my example programs the same way we did back in 1974 - I wrote a COBOL program to re-organize the file. Actually a pair of programs, one to read the file sequentially copying all the records with "A" in position one to a sequential file and a second to read the sequential file and reload the ISAM file. Not a great deal of trouble in a production environment since you need this type of reorganization process anyway, but an annoyance with what should have been a couple of simple example programs.
This page was last updated on January 17, 2015.