Page 1 of 1

AMODE 31 Programming in Assembler

PostPosted: Wed Mar 02, 2016 5:44 am
by steve-myers
Over in the Cobol section a user asked what does this AMODE and RMODE business mean? A similar query can be made for us Assembler programmers: what do I have to do differently to make my program AMODE 31, or AMODE 31, RMODE ANY?

Let's take a look at a minimal data set copy program.
MCOPY    CSECT                     Define program CSECT
         USING *,12                Establish program addressability
         SAVE  (14,12),,*          Save registers
         LR    12,15               Prepare program vase register
         LA    15,SAVEAREA         Compute new save area address
         ST    15,8(,13)           Add new save area to the
         ST    13,4(,15)            save area chain
         LR    13,15               Prepare new save area pointer
         OPEN  (INDCB,INPUT,OUTDCB,OUTPUT)  Open the data sets
COPYLOOP GET   INDCB               Read an input record
         LR    0,1                 Copy the record address to reg 0
         PUT   OUTDCB,(0)          Write the output record
         B     COPYLOOP            Do it again
EOF      CLOSE MF=(E,CPARM)        Close the data sets
         LA    2,CPARM             Load address of the parameter list
CLL0100  L     1,0(,2)             Load a DCB address
*        N     1,=A(X'FFFFFF')     Isolate the 24-bit DCB address
         FREEPOOL (1)              Free the buffer pool
         TM    0(2),X'80'          End of the parameter list?
         LA    2,4(,2)             Compute address of next DCB pointer
         BZ    CLL0100             Br if not end of the parameter list
         L     13,4(,13)           Load address of the higher save area
         RETURN (14,12),T,RC=0     Restore registers & return
CPARM    CLOSE (INDCB,,OUTDCB),MF=L
INDCB    DCB   DSORG=PS,MACRF=GL,DDNAME=INPUT,EODAD=EOF
OUTDCB   DCB   DSORG=PS,MACRF=PM,DDNAME=OUTPUT
SAVEAREA DC    18F'0'
         DC    0D'0'
         LTORG ,
         DC    0D'0'
         END   MCOPY
One of the changes is already hinted at in the code. Many data areas in OS/360 specified an address as 24 bits, and used the remaining 8 bits of the 32-bit word for other purposes. The I/O data areas such as the DCB and the parameter lists for the data set open and close services are particularly nasty in this regard. A beginner, with no knowledge of the history of z/OS may question why this was done.

The answer is that old problem: storage. In 1964 and 1965, when OS/360 was designed and implemented, storage was very expensive. One byte of storage cost roughly one US dollar. The dreamers that were marketing System/360 had the dream that there would be one operating system for all systems. The engineers building what became OS/360 used every trick they could imagine to cut OS/360's footprint. One of the tricks was to use the high order byte of a full word used as an address for other data. For example -
                                     2          OPEN  (ADCB,OUTPUT),MF=L
000000                                3+         DC    0F'0'
000000 8F                             4+         DC    AL1(143)
000001 000000                         5+         DC    AL3(ADCB)
                                      6          CLOSE (ADCB,LEAVE),MF=L
000004                                7+         DC    0F'0'
000004 B0                             8+         DC    AL1(176)
000005 000000                         9+         DC    AL3(ADCB)
The high order byte of the DCB address specifies the options. In the OPEN parameter list, the options are 0X8F. The first bit indicates the last DCB address in the parameter list, the next three bits – 000 in our example are used for other options, and the F in 0X8F indicates output. This pattern is repeated in the CLOSE parameter list.

The DCB repeated this. There are many addresses in the DCB. With one exception – not used until MVS/XA and OS/390 – all these addresses use the high order byte for something else. For example -
00002C 00                            31+         DC    BL1'00000000'           BFTEK,BFLN,HIARCHY
00002D 00000A                        32+         DC    AL3(EOF)                EODAD

Another trick in the DCB was to not assemble unused data areas. For example -
                                    11 ADCB     DCB   MACRF=E,DDNAME=EXCPDD


                                     14+*                       DATA CONTROL BLOCK
                                     15+*
000028                00028 00000    16+         ORG   *-40                    TO ELIMINATE UNUSED SPACE
000000                               17+ADCB     DS    0F'0'                    ORIGIN ON WORD BOUNDARY
000000                00000 00028    18+         ORG   *+40                    TO ORIGIN GENERATION

                                     20+*                       FOUNDATION BLOCK

000028 C5E7C3D7C4C44040              22+         DC    CL8'EXCPDD'             DDNAME
000030 02                            23+         DC    BL1'00000010'           OFLGS
000031 00                            24+         DC    BL1'00000000'           IFLG
000032 8000                          25+         DC    BL2'1000000000000000'   MACR
Yet another trick was to simply overlay part of the DCB in OPEN, and restore it in CLOSE. The DD name data, for example, disappears and is replaced by (yet another) truncated address and other data that is not seen in the DCB data area as shown here.

The little copy program at the start of the post runs fine as an AMODE 24, RMODE 24 program. It takes two changes to run AMODE 31, RMODE 24:
  • Add an AMODE 31 Assembler statement. It can be linked AMODE 31 without the statement, but it won't run.
  • Remove the * from the N 1,=A(X'FFFFFF') statement. This statement removes the CLOSE macro option, leaving the 24-bit DCB address. A wise programmer always uses this statement, so the same technique is used for both an AMODE 24 and an AMODE 31 program.

In a few days I will post the little copy program revised to run RMODE ANY.

Re: AMODE 31 Programming in Assembler

PostPosted: Sat Mar 05, 2016 4:46 am
by steve-myers
As promised a few days ago, here is the simplified copy program arranged to run AMODE 31, RMODE ANY.
MCOPY    CSECT                     Define program CSECT
MCOPY    AMODE 31
MCOPY    RMODE ANY
         USING WORKAREA,11         Establiszh work area addressability
         USING *,12                Establish program addressability
         SAVE  (14,12),,*          Save registers
         LR    12,15               Prepare program base register
         LA    5,WASIZE            Load length of the work area
         GETMAIN R,LV=(5)          Allocate storage for the work area
         LR    4,1                 Copy work area address to reg 4
         LR    11,1                  and reg 11
         SR    15,15               Set reg 15 = 0
         MVCL  4,14                Clear the work area
         LA    15,SAVEAREA         Compute new save area address
         ST    13,4(,15)           Add new save area to the
         ST    15,8(,13)            save area chain
         LR    13,15               Prepare new save area pointer
         MVC   OPARM,MASTOPEN      Initialize the Open parameter list
         MVC   INDCB,MASTDCB       Initialize the input DCB
         MVC   INDCBE,MASTDCBE     Initialize the input DCBE
         MVC   OUTDCB,MASTDCB      Initialize the output DCB
         LA    0,INDCBE            Store the DCBE address in the DCB
         ST    0,(DCBDCBE-IHADCB)+INDCB
         MVC   (DCBDDNAM-IHADCB)+OUTDCB,=CL8'OUTPUT'  Initialize the  ->
                                                       output DD name
         OPEN  (INDCB,,OUTDCB),MF=(E,OPARM)  Open the DCBs
LOOP     GET   INDCB               Get an input record
         LR    0,1                 Copy the record address to reg 0
         PUT   OUTDCB,(0)          Write the output record
         B     LOOP                Do it again
EOF      MVC   OPARM,MASTCLOS      Initialize the Close parameter list
         CLOSE (INDCB,,OUTDCB),MF=(E,OPARM)  Close the DCBs
         LA    2,OPARM             Load address of the Close param list
CLL0100  L     1,0(,2)             Load a DCB address
         N     1,=A(X'FFFFFF')     Isolate the 24-bit DCB address
         FREEPOOL (1)              Free the buffer pool
         TM    0(2),X'80'          End of the parameter list?
         LA    2,4(,2)             Compute addr of the next DCB pointer
         BZ    CLL0100             Br if not the end of the parm list
         L     13,4(,13)           Load address of the higher save area
         LA    0,WASIZE            Load length of the work area
         FREEMAIN R,LV=(0),A=(11)  Free the work area
         RETURN (14,12),T,RC=0     Restore registers & return
MASTOPEN OPEN  (*-*,INPUT,*-*,OUTPUT),MF=L  Master OPEN parm list
MASTCLOS CLOSE (*-*,,*-*),MF=L              Master CLOSE parm list
MASTDCBE DCBE  EODAD=EOF           Master DCBE
MASTDCB  DCB   DSORG=PS,MACRF=(GL,PM),DDNAME=INPUT,DCBE=*-*  Master DCB
         DC    0D'0'
         LTORG ,
         DC    0D'0'
WORKAREA DSECT                     Define the work area
SAVEAREA DS    9D                  72 byte register save area
OPARMS   OPEN  (*-*,,*-*),MF=L     Open/ Close parameter list
OPARM    EQU   OPARMS,*-OPARMS
INDCBS   DCB   DSORG=PS,MACRF=(GL,PM),DDNAME=INPUT,DCBE=*-*
INDCB    EQU   INDCBS,*-INDCBS
OUTDCBS  DCB   DSORG=PS,MACRF=(GL,PM),DDNAME=INPUT,DCBE=*-*
OUTDCB   EQU   OUTDCBS,*-OUTDCBS
INDCBES  DCBE  EODAD=*-*
INDCBE   EQU   INDCBES,*-INDCBES
         DS    0D
WASIZE   EQU   *-WORKAREA
         DCBD  DSORG=QS,DEVD=DA
         END   MCOPY
Those of you who have been programming a while will note that this program is coded like a reenterable program. Well, it is reenterable: the added cost is so small as to make no difference.

The second thing a beginner will notice is the program uses a DCBE, a DCB extension. What is this new, strange beast? The DCBE serves several purposes. Obviously it adds some new capabilities, which we are not using here. In addition, it provides a mechanism to provide 31 bit addresses for selected addresses, like the end of data routine, that cannot be specified in the DCB because the corresponding data area in the DCB has just 24 bits.

As we have previously mentioned, all DCB data areas you are actually using must be below the line. Traditional parameter lists for OPEN and CLOSE must also be below the line because of those pesky 24-bit addresses.

The code specifies one DCB in the program text -

MASTDCB DCB DSORG=PS,MACRF=(GL,PM),DDNAME=INPUT,DCBE=*-*

As coded, no address constants are actually used so it can be coded even though the program text is above the line. The DCBE parameter is specified because it sets flag bits that a DCBE address might be present.

MACRF=(GL,PM) permits the DCB image to be used for both input and output, though not at the same time. It does make it possible to use a single prototype in the program for both real DCBs in the work area.

The Program

LA 5,WASIZE
GETMAIN R,LV=(5)
LR 4,1
LR 11,1
SR 15,15
MVCL 4,14

Like most reenterable programs, the first thing the program does is allocate and initialize the work area. Strictly speaking, the MVCL instruction to initialize the entire work area is not necessary here since most of the work area consists of DCBs and a DCBE that are initialized elsewhere, but it's a good idea to do this all the time.

The GETMAIN macro allocates storage for the work area below the line. The GETMAIN service has a long history.

The only positional parameter – R – specifies the type of GETMAIN. If you look up the GETMAIN macro discussion in MVS Programming: Assembler Services Reference, Volume 1 you will discover a large number of these parameters. Most go back to OS/360 and are rarely used any more. The remainder were introduced with the first MVS release. The only OS/360 parameter still commonly used is R. Since it's an OS/360 parameter, the storage it allocates will be below the line. A more “recent” GETMAIN would be -

GETMAIN RU,LV=(0),LOC=(24,ANY)

MVC OPARM,MASTOPEN
MVC INDCB,MASTDCB
MVC INDCBE,MASTDCBE
MVC OUTDCB,MASTDCB
LA 0,INDCBE
ST 0,(DCBDCBE-IHADCB)+INDCB
MVC (DCBDDNAM-IHADCB)+OUTDCB,=CL8'OUTPUT'

Addresses specified like (DCBDCBE-IHADCB)+INDCB are a way to avoid code like

LA 1,INDCB
USING IHADCB,1
ST 0,DCBDCBE
DROP 1

DCBDCBE-IHADCB specifies the offset of the data area in the DCB, INDCB specifies the DCB being modified.

LOOP GET INDCB
LR 0,1
PUT OUTDCB,(0)
B LOOP

The copy loop is identical to the copy loop in the prototype AMODE 24, RMODE 24 program.

MASTOPEN OPEN (*-*,INPUT,*-*,OUTPUT),MF=L

This is the prototype of the parameter list for the OPEN macro. The *-* generates a value of 0 and is a visual indication a value will be stored later.

MASTDCBE DCBE EODAD=EOF

MASTDCBE specifies the prototype for a DCBE that is copied to the work area. Unlike the DCB, a DCBE can be specified above the line, so why move it? It has to be moved because OPEN modifies it. Since MCOPY is supposed to be reenterable, the copy in MCOPY's program text cannot be modified, so it is moved.

OPARMS OPEN (*-*,,*-*),MF=L
OPARM EQU OPARMS,*-OPARMS

These statements define the space for the parameter list for the OPEN and CLOSE macros. The EQU statement effectively redefines the parameter list; *-OPARMS defines a length value for the OPARM symbol so that MVC OPARM,MASTOPEN will copy the correct number of bytes to OPARM. The same technique is used for the two DCBs and the DCBE.
WORKAREA DSECT                     Define the work area
SAVEAREA DS    9D                  72 byte register save area
OPARMS   OPEN  (*-*,,*-*),MF=L     Open/ Close parameter list
OPARM    EQU   OPARMS,*-OPARMS
INDCBS   DCB   DSORG=PS,MACRF=(GL,PM),DDNAME=INPUT,DCBE=*-*
INDCB    EQU   INDCBS,*-INDCBS
OUTDCBS  DCB   DSORG=PS,MACRF=(GL,PM),DDNAME=INPUT,DCBE=*-*
OUTDCB   EQU   OUTDCBS,*-OUTDCBS
INDCBES  DCBE  EODAD=*-*
INDCBE   EQU   INDCBES,*-INDCBES
         DS    0D
WASIZE   EQU   *-WORKAREA
Accepted practice in the 1960s for reenterable programs was for the 72 byte register save area to be at the start of the work area, as it is here, and for register 13 to define the start of the work area as well as the start of the register save area. This practice continued to become the root practice for execution environments for some high level languages such PL/I and C/370 and continued to become the practice in the Language Environment execution environment. However, Assembler programmers largely abandoned this model. Register 13 still pointed to a register save area, but it was no longer tied to an immovable work area; another register pointed to the work area. It was much more convenient for register 13 to be used for a single purpose. While it is not necessary for MCOPY, the work area pointer (register 11) is separate from the save area pointer, (register 13, as usual). This was effectively true in the original non-reenterable version of MCOPY in the first post; register 12 was effectively the work area pointer as well as the program base register.