MVS TOOLS AND TRICKS OF THE TRADE August 1995 Sam Golob MVS Systems Programmer sbgolob@cbttape.org Sam Golob is a Senior Systems Programmer A NOVICE'S GUIDE TO ASSEMBLER PROGRAMMING - PART I I hear that the trend today among new systems programmers, is that you don't really have to know Assembler language coding. It is true that one can get by with a minimal knowledge of Assembler. Normally, to install some product, you just have to "slap it in" and customize it a bit. To install the operating system, you just have to "follow the CBIPO yellow brick road" and adjust a lot of PARMLIB members. Assembler coding doesn't often enter into the procedures. And it seems that many of the newer people have been managing to do without it. But the truth is that if you don't know Assembler, you're missing a lot of the picture and a lot of the joy. The operating system, whether it be written in PL/AS or Assembler, is still written in Assembler from our point of view. When some instruction causes a problem, it's nice to be able to look at a dump of the module, and be able to talk intelligently. Most of all, there will always exist a lot of user written interfaces to operating system services. IBM and the software vendors will not always fill every need. It provides much personal satisfaction to dig into one of these utilities and see for yourself, how it does its "magic". Then you can enhance it, so it can do even more. In this piece, I'm not going to cover any intricacies. This might be the first Assembler article you've seen which is really simple. If you know a few facts about how an Assembler program works, and you "break the ice", your usefulness to the shop will increase measurably. Furthermore, you'll begin to experience some of the real joy that our systems programming craft has to offer. I've coded a sample program called MYID, with all the frills left out. But the program works and it proves my point. This is a TSO command that has no parameters. You just type in the command MYID from a TSO session, and the command returns a message: MY USERID IS yourid, with the correct id filled in. This program will show you how easy it really is to follow a chain of address pointers in control blocks. If you learn to understand this simple logic, you will soon be able to extract very serious information from the MVS operating system. Now we'll talk about basics. REGISTERS. An IBM 370/390/9000 type computer has sixteen four-byte (fullword) compartments in its mechanism, called registers. These sixteen "guys" are available to hold quantities which are necessary for every program's execution. I'm not going to talk about "control registers" or "access registers" or any of the complicated stuff here, just the plain old "general purpose registers" that every program uses. You can consider the 16 registers as a part of the computer hardware which all programs have access to. A program can put any quantity into any register. However, for the program to do what you want, you have to control the quantities put into the registers very carefully. The sixteen general purpose registers are called "Register 0" thru "Register 15". The contents of each register are treated as a binary or hexadecimal number, which can go from zero to 4 gigabytes. In some instructions, the high order bit X'80000000' might signify a sign, but not always. Oftentimes an instruction treats a register as a 32-bit unsigned binary number, and this is frequently the case. For accuracy, please refer to the IBM Principles of Operation manual regarding each instruction you want to use. These 16 registers are used by Instructions. A list of all the Machine Instructions is found in the IBM manual called Principles of Operation. I'm sure that most of you have been intimidated by this manual. I can surely say that I have. Now, after all my years in this field, I am able to make some sense out of the Principles of Operation. I hope to provide a few hints, so that your trips through this useful manual will often yield some tasty fruit, instead of constant frustration. BASE AND DISPLACEMENT. A program consists of instructions in sequence, one after another. However, sometimes the instructions should not be executed in the direct linear sequence, but the logic calls for jumping, or "branching" to a new place within the program. Other parts of the program logic may require reference to data or instructions that are located elsewhere in the program. Therefore, each program must keep some mechanism for determining the exact location of any of its parts. This location tracking mechanism is called "base and displacement". The way it works is as follows: One or more registers are chosen as "base registers". Each base register has the capacity to track 4096 bytes (the size of zero thru X'FFF' bytes) in the program, further than where it starts. If more bytes need to be tracked (e.g. the program is larger than 4096 bytes), then more base registers may need to be used. The ASSEMBLER (the Compiler for Assembler language is called the Assembler) has an instruction called USING. This USING instruction takes as operands, a starting location and one or more registers. The USING instruction says that from the designated starting location in the program, use each of the designated register(s) for counting all locations up to 4096 bytes further on. If there is more than one register being defined in one USING statement, they are strung out one after the other, in their order of mention. The USING instruction is not enough. All base registers need to be loaded with the actual address in virtual storage which corresponds to the location in the program which the USING instruction designates as its starting point. If the base registers don't get properly loaded, the program won't run. We will now explain how this loading is accomplished. It is usually done with LOAD, LOAD ADDRESS, or LOAD REGISTER instructions. LOAD AND LOAD ADDRESS. The USING instruction we just mentioned, is not a machine instruction. It is just a directive to the Assembler how to calculate displacements from a base location. Now we will introduce four actual machine instructions which can be executed by the computer hardware. These instructions are LOAD (designated by the symbol "L"), LOAD ADDRESS (designated by "LA"), LOAD REGISTER (designated by "LR") and STORE (designated by "ST"). It is well worth remembering that LOAD and STORE are a pair which do the exact opposites. All of these instructions are used in the sample program shown in Figure 1. LOAD takes the contents of a fullword (four bytes in storage aligned on a boundary location which is divisible by four) and dumps it into a register, unchanged. Remember that LOAD does not change anything. It just takes the four bytes from storage and dumps it into the four bytes of the register, where the computer can work on it. STORE does exactly the opposite. STORE takes the 4-byte content of a register, and dumps it unchanged into a fullword storage location. This may seem utterly simple, but you should go over it about twenty times in your mind. I have a reason for saying so. It is very easy to confuse the roles of the LOAD and LOAD ADDRESS instructions. After all these years, I still have to be careful myself. Once you realize that LOAD does the exact opposite of STORE, you'll be much more able to tell LOAD and LOAD ADDRESS apart. LOAD REGISTER just dumps the contents of one register into another, completely erasing the previous contents of the target register. If you need to save the contents of one register to a different register, you use LR. All these instructions have two operands. In the Principles of Operation manual, the operands of each instruction are always clearly described as the first or second (or third) operand. Usually, data movement goes from the second operand to the first. However, with the STORE instructions, the opposite happens, and the movement goes from the first operand to the second. Therefore, even though LOAD and STORE do the opposite thing, the corresponding instructions look identical, except for the instruction itself. Now we have to talk about the LOAD ADDRESS instruction. LOAD ADDRESS puts a numeric quantity into a register. This quantity is added up from three sources. These are: another register called the base, a numeric quantity from 0 to 4095 (X'000' thru X'FFF') called the displacement, and a second register called the index register. Much of the time, the index register is zero, so the quantity calculated is the sum of the base register quantity, and the displacement. LOAD ADDRESS is most often used to calculate a new address from an old address, or it can be used as a binary summation tool. It should be noted that the highest order bit (in 31-bit mode) or the highest-order byte (in 24-bit mode) is zeroed by the LOAD ADDRESS instruction. Why do people confuse the LOAD and LOAD ADDRESS instructions? Because they look the same. But the difference is that LOAD references a storage location, and LOAD ADDRESS doesn't, it only calculates a new number from old ones. LOAD ADDRESS does not have anything to do with storage at all. Now how are these instructions actually used to put the proper virtual storage address in a USING register? There are several ways. But to understand things properly, we must first consider how almost any program executes in an MVS system. If you'll take an actual look at how a program runs in MVS, it will open your eyes. Most programs run as called programs of called programs, nested many times, program under program. If you'd look at a list of these programs, your own program might be the ninth or tenth one. All these programs must properly communicate their linkage to each other, or great confusion would result. Because of this, there is what is called a STANDARD OS LINKAGE CONVENTION, which is almost always followed when programs are run under MVS. MVS PROGRAM LINKAGE CONVENTIONS. The standard linkage convention confuses most novices for a long time. However, if you look at it in the right way, it is not hard to grasp. It is like this. Every program has to establish an 18-fullword (72-byte) save area. This is for saving 15 of the previous program's 16 registers in the last 15 of the 18 fullwords. We save the previous program's registers 14 and 15, and 0 thru 12, in that order. Register 13 is special. Register 13 must always point to a save area, either our program's save area or the save area of the previous program. One other fact has to be mentioned. When our program receives control from the previous program, Register 15 contains the entry point address of our program. That is why the initial base register to our program can be loaded with a LOAD REGISTER instruction, dumping the contents of Register 15 into the register which our program will use as its initial base register. For example, in our sample program, we use Register 12 as our one base register. Therefore, to set up Register 12 properly, we do a LR R12,R15 to dump the contents of Register 15, which has the entry point address, into Register 12, which will track subsequent program locations from there on. Getting back to the linkage conventions, Register 13 comes in containing the address of the previous program's save area. When we establish our new save area, we preserve the previous contents of Register 13, point Register 13 to our new save area, and store the address of the previous save area in the second fullword (4 off the beginning) of our new save area. Thus, Register 13 is adjusted to point to our new save area, and the old one's address is preserved at 4 off the new area. We also have to inform the old program, which isn't actually running now, about where our own new save area is. We do this by storing the value of our current Register 13 (pointing to our own save area) into the third fullword (8 off the beginning) of his save area. Look at the sample program to see how this is done. When our program has finished executing, and we have to return control to the previous program, we do as follows. First we point Register 13 to the old program's save area. Then we restore the old program's registers from the last 15 fullwords of our save area. Finally, we give control to whatever program is located at the address of the old program's Register 14. This Register 14 should contain the entry point where the old program wants to get control back. Please study the sample program in Figure 1. Type it into your system. Assemble and linkedit it, and try to run it as a TSO command. If you haven't been too familiar with Assembler language until now, you should get a big boost by doing this. Good luck. We'll see you next month. * * * * * * * * * * * * * * * * * * * * * * * Figure 1. MYID is a simple Assembler program which is meant to run under TSO. You assemble and linkedit this program and put it into a load library that is defined to your TSO session, perhaps one of the ISPLLIB libraries. The program is called MYID. Under your TSO session, this TSO command MYID will return the phrase MY USERID IS yourid, with your correct userid plugged in. I didn't make MYID re-entrant, so don't put MYID into LPALIB. We illustrate how to run a chain of control blocks here. Register 3 is loaded with the address of the CVT. Then Register 3 is made to point to the address of the TCB words; then it is made to point to the current TCB, then to the JSCB, and finally to the PSCB. All this is done with LOADs of address pointers that are in these control blocks. The beginning of the PSCB contains your TSO session's userid. A MOVE CHARACTER instruction moves 7 bytes from the PSCB control block containing the userid into a blank space of the message area that we've defined. The blanks begin 13 bytes off the beginning of the message area. Finally, the TPUT macro is run against the modified message area, to write the entire message to your screen. You can verify the displacements I've coded by looking into the MVS Data Areas manuals under CVT, IKJTCB, IEZJSCB, and IKJPSCB. The first parameter of the TPUT macro is the address of the beginning of the message. The second parameter is the length of the message. I have purposely not used DSECTs or mapping macros, because I did not want to unnecessarily complicate matters for this illustration. * M Y I D P R O G R A M - A T S O C O M M A N D * * TSO COMMAND PROCESSOR TO DISPLAY THE USERID OF THE INVOKER. * REGISTER EQUATES R0 EQU 0 R R1 EQU 1 E R2 EQU 2 G R3 EQU 3 I R4 EQU 4 S R5 EQU 5 T R6 EQU 6 E R7 EQU 7 R R8 EQU 8 R9 EQU 9 E R10 EQU 10 Q R11 EQU 11 U R12 EQU 12 A R13 EQU 13 T R14 EQU 14 E R15 EQU 15 S MYID CSECT STM R14,R12,12(R13) SAVE CALLER'S REGISTERS R14 THRU R12 LR R12,R15 LOAD ENTRY POINT INTO BASE REGISTER USING MYID,R12 TELL THE ASSEMBLER, R12 IS THE BASE LR R15,R13 SAVE CALLER'S SAVEAREA ADDRESS LA R13,SAVE POINT R13 TO OUR SAVEAREA ADDRESS ST R15,SAVE+4 STORE HIS SAVEAREA INTO MINE + 4 ST R13,8(,R15) STORE MINE INTO HIS SAVEAREA + 8 RUNCHAIN L R3,16 POINT TO CVT. ADDR IS IN LOW STORAGE L R3,0(,R3) POINT TO TCB/ASCB WORDS, "0" OFF CVT L R3,4(,R3) POINT TO TCB, "4" OFF TCB/ASCB WORDS L R3,X'B4'(,R3) POINT TO JSCB. X'B4' OFF CURRENT TCB L R3,X'108'(,R3) POINT TO PSCB. X'108' OFF THE JSCB MVC MSGLINE+13(7),0(R3) MOVE USERID IN FROM 0 OFF THE PSCB TPUT MSGLINE,L'MSGLINE DISPLAY THE WHOLE MESSAGE ON THE TUBE RETURN DS 0H L R13,SAVE+4 RELOAD CALLER'S SAVEAREA POINTER LM R14,R12,12(R13) RELOAD THE CALLER'S REGISTERS BR R14 RETURN TO CALLER SAVE DC 18F'0' DEFINE MY SAVEAREA - 18 FULLWORDS MSGLINE DC CL20'MY USERID IS ' DEFINE LINE FOR MESSAGE END