Assembler course

I thought I might explain the basics of IBM mainframe assembler, 

but it's much harder than I thought. Anyway, here's what I wrote.


If your masochistic nature is insufficiently satisified, then maybe:

Introduction to IBM assembler.  You have lots of memory these days.

And you have registers (that hold #s or addresses).

Maybe first, learn to count. You count 1,2,3,4,5,6,7,8,9,10,11

In hex, we count  0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10

Always start with 0, and  A-F  = 10-15 in decimal.  X'10' is dec-16.

(At 81 years old, I'm  x'51'.  Doesn't that sound better.~)


Your code is never loaded at loc=0, so what you want to do is have

a register that points to your code no matter where it gets loaded

in memory.  Instructions reference memory locations in what is called

base-displacement format.  IE whatever is in a register, plus an 'offset'

from the address in the register.  Here's an instruction.

MVC   A,B   Move what ever is at  B  to  A.   (yes, they do it backwards.)

The generated executable code, using reg-12 as the base register is


D21EC0A1C0D3  move 31 bytes to reg-12+161, from reg-12+207


D2 = move character inst.

1E = 30 in hex. Intructions like this move the length+1 (D200 moves 1 byte)

C0A1 = C=reg-12 + 0A1 so reg-12 + 0A1 in hex converts to 161 in decimal.

C0C3 = C=reg-12 + 0C3 so reg-12 + 0C3 in hex converts to 207 in decimal.


Another thing to learn is that some program calls your program, (starts

your program) and your program might call another program (have that

program do something for your program.  First we'll look at simple copy.


         OPEN  (INFILE,INPUT,OUTFILE,OUTPUT)  open 2 files

GET      GET   INFILE             read a record, put it's address in reg-1

         LR    R0,R1              put the record address into reg-0

         PUT   OUTFILE,(R0)       write that record

         B     GET                branch to label GET.

END      CLOSE (INFILE,,OUTFILE)  at end-of-file, close the 2 files


That would be a real program that would work, but remember, I said

that some program calls (starts) your program, and in some cases,

your program might call another.  What we need are standard

LINKAGE CONVENTIONS.   We need to save the registers of whoever

called us, in a  SAVEAREA  that he provides, and we need to

establish a savearea that someone we call can use to save

our registers (0-15). We also need to save the address of out caller's

savearea, and set it up so whoever we call can do the same.

BTW,  GET and  PUT  are called routines, and will use our savearea.


Savearea format (in WORDS.  A word is 4 BYTES (characters) of storage)

Word 1 - not used (generally)

Word 2 - your caller's savearea address

Word 3 - address of the saveare of whoever you call

Words 4-19 will contain the contents of reg-14 thru reg-12 (wrap around)


YOURPGM  START 0             Start your program

         STM   14,12,12(13)  Store callers regs

         BALR  12,0          load the addr of the next inst in reg-12

         USING *,12          tell the assembler were reg-12 points

         LA    2,SAVEA       load addr of your savearea

         ST    2,8(13)       store a(your savea) in caller's savea

         ST    13,4(2)       store a(caller's savea) in yours

         LR    13,2          put the savea address in reg-13 (always)

         OPEN .....          etc.


And then when you're all done, clean up and return to caller


END      CLOSE (INFILE,,OUTFILE)  Close the files

         L     13,4(13)           load addr of caller's savea

         LM    14,12,12(13)       load callers regs

         SR    15,15              reg-15 =0  is a successful return code

         BR    14                 go back to caller.


We now have nearly all the parts to make a successful program.

But we haven't defined the input and output files.

We do that with a DCB or Data Control Block.

INFILE   DCB   DDNAME=INPUT,DSORG=PS,DEVD=DA,MACRF=GL,EODAD=END

OUTFILE  DCB   DDNAME=OUTPUT,DSORG=PS,DEVD=DA,MACRF=PM


To put the whole thing together:


YOURPGM  START 0             Start your program

         YREGS ,             so you can use r1 for reg-1, etc.

*                                     (asterisk in col-1 = comment)

         STM   14,12,12(13)  Store multiple (save) user's regs

         BALR  12,0          load the addr of the next inst into reg-12

         USING *,12          tell the assembler were reg-12 points

         LA    2,SAVEA       load addr of your savearea

         ST    2,8(13)       store a(your savea) in caller's savea

         ST    13,4(2)       store a(caller's savea) in yours

         LR    13,2          put the savea address in reg-13 (always)

         OPEN  (INFILE,INPUT,OUTFILE,OUTPUT)  open 2 files

*

GET      GET   INFILE             read a record, put address in reg-1

         LR    R0,R1              put the record address into reg-0

         PUT   OUTFILE,(R0)       write that record

         B     GET                branch to label GET.

*

END      CLOSE (INFILE,,OUTFILE)  Close the files

         L     13,4(13)           load addr of caller's savea

         LM    14,12,12(13)       load multiple, callers regs

         SR    15,15              reg-15=0  is a successful return code

         BR    14                 go back to caller.

*

SAVEA    DC    18F'0'             SAVEAREA

OUTFILE  DCB   DDNAME=OUTPUT,DSORG=PS,DEVD=DA,MACRF=PM

INFILE   DCB   DDNAME=INPUT,DSORG=PS,DEVD=DA,MACRF=GL,EODAD=END

         END   YOURPGM


Every file has characterists. In mainframe land, they are:

LRECL=####     logical record length

BLKSIZE=####   number of bytes in a block of records

RECFM=??       Record Format.

Let's suppose we have a file of 80 byte (card image) records

LRECL=80,BLKSIZE=2400,RECFM=FB  80 byte recs, 30/block, all recs the same.


You'd attach that line to the OUTFILE  DCB  unless you're copying to an

already existing file.  In that case, the system already knows that info.

You have a working program.  Let's add 1 more thing - a routine to copy

the LRECL, BLKSIZE, RECFM from INPUT to OUTPUT, only if needed.


EXITLIST DC    X'87',AL3(EXITLIST+4)

         USING *,15

         USING IHADCB,1

         CLI   DCBRECFM,0       do we need to copy?

         BNER  14               no, just return.

         MVC   DCBRECFM,DCBRECFM-IHADCB+INFILE  yes, do the copy

         MVC   DCBLRECL,DCBLRECL-IHADCB+INFILE

*        MVC   DCBBLKSI,DCBBLKSI-IHADCB+INFILE  (system will pick blksi

         BR    14

OUTFILE  DCB   DDNAME=OUTPUT,DSORG=PS,DEVD=DA,MACRF=PM,EXLST=EXITLIST


You need to put this code right before the 2 DCB statements.

And, right before the end of your program, code:

         DCBD  DEVD=DA


Now you have a pretty simple  KISS program.

What's KISS?  Keep It Simple Stupid.  The good programmer's mantra.


I've been doing this 50 years. Email me to tell me what I forgot.

or what is not understandable.

--------------------------------------------------------------

         AGO   .START   (tell the assembler to skip these comments)


IF YOU WANT TO TRY THIS ASSEMBLER PROGRAM, THEN YOU COULD:

GO TO  Z390.ORG AND CLICK  Z390 V1.8.1 IS NOW AVAILABLE FOR DOWNLOAD

AND DO IT.   I PUT ALL MY STUFF IN

C:\USERS\LIN\DOCUMENTS\Z390CODE\   (USE YOUR NAME)


CREATE A FILE C:\USERS\LIN\DOCUMENTS\Z390CODE\YOURPGM.BAT AND CONTAINS


SET  G=C:\USERS\LIN\DOCUMENTS\Z390CODE\yourpgm

SET       INPUT=%G%.PRN.TXT

SET      OUTPUT=%G%.OUT.TXT

BAT\ASMLG %G%.MLC TIME(1)


WHEN YOU'VE DONE THAT, START Z390 AND ENTER:


C:\USERS\LIN\DOCUMENTS\Z390CODE\YOURPGM


WHAT THAT SHOULD DO IS COPY YOUR LISTING TO THE .OUT.TXT FILE


.START   ANOP

*

YOURPGM  START 0             Start your program

         YREGS ,             so you can use r1 for reg-1, etc.

*                                     (asterisk in col-1 = comment)

         STM   14,12,12(13)  Store multiple (save) user's regs

         BALR  12,0          load the addr of the next inst into reg-12

         USING *,12          tell the assembler were reg-12 points

         LA    2,SAVEA       load addr of your savearea

         ST    2,8(13)       store a(your savea) in caller's savea

         ST    13,4(2)       store a(caller's savea) in yours

         LR    13,2          put the savea address in reg-13 (always)

         OPEN  (INFILE,INPUT,OUTFILE,OUTPUT)  open 2 files

*

GET      GET   INFILE             read a record, put address in reg-1

         LR    R0,R1              put the record address into reg-0

         PUT   OUTFILE,(R0)       write that record

         B     GET                branch to label GET.

*

END      CLOSE (INFILE,,OUTFILE)  Close the files

         L     13,4(13)           load addr of caller's savea

         LM    14,12,12(13)       load multiple, callers regs

         SR    15,15              reg-15=0  is a successful return code

         BR    14                 go back to caller.

*

SAVEA    DC    18F'0'             SAVEAREA

OUTFILE  DCB   DDNAME=OUTPUT,DSORG=PS,DEVD=DA,MACRF=PM

INFILE   DCB   DDNAME=INPUT,DSORG=PS,DEVD=DA,MACRF=GL,EODAD=END

         END   YOURPGM