How to let a macro do all the tedious coding for you
This time I will make a macro with you that can be used to make all the tedious work at the start of the program and at the end. I will show the macro in its full at last in the blog. You will then be able to copy and paste it to your own system and tailor it to your needs.(*1)
The Macro is called PROGRAM and it takes four states which are coded right after PROGRAM
PROGRAM EQU
|
(not mandatory but strongly recommended)
Defines the register EQU | ||
[DSECT name]
|
PROGRAM STORAGE
|
[,STORAGEREG=]
|
First occurrence. Coded before and after your fields
STORAGEREG=[register]
Default is ‘R12’. Base reg for storage DSECT
|
PROGRAM STORAGE
|
Second occurrence. Calculates the length of your storage
| ||
program name
|
PROGRAM START
|
[,SAVEAREA=]
[,SADSECT=]
|
SAVEAREA=[name of savearea]. Default SAVEAREA’
SADSECT=[Name of the savearea DSECT. Default ‘SA’
|
return label
|
PROGRAM END
|
[,RETURNCODE=]
|
RETURNCODE=[return code to calling program.]
Default ‘0’ |
Examples
A minimum program could look like this:
PROGRAM EQU 00010000
PROGRAM STORAGE 00020000
HW DS 0F 00020100
HWLEN DS H 00020200
HWTEXT DS CL16 00021000
PROGRAM STORAGE 00030000
PGM3 PROGRAM START 00040000
MVC HWTEXT,=CL(L'HWTEXT)'HELLO WORLD' 00050002
MVC HWLEN,=Y(L'HWTEXT) 00060000
LA R2,HW 00061002
WTO TEXT=(R2),ROUTCDE=11 00070002
RETURN PROGRAM END 00080002
END 00090000
|
and the output will be this.
19.51.28 JOB05893 +PGM3 - 09/17/15-19.51
19.51.28 JOB05893 +HELLO WORLD
|
As you can see. Coding gets much easier.
Assembler list
It is much easier to understand with examples. I show you (or some of) the results of each invocation of the macro PROGRAM. One example per state.
EQU
1 PROGRAM EQU
00000 2+R0 EQU 0 01-PROGR
00001 3+R1 EQU 1 01-PROGR
00002 4+R2 EQU 2 01-PROGR
00003 5+R3 EQU 3 01-PROGR
00004 6+R4 EQU 4 01-PROGR
00005 7+R5 EQU 5 01-PROGR
00006 8+R6 EQU 6 01-PROGR
00007 9+R7 EQU 7 01-PROGR
00008 10+R8 EQU 8 01-PROGR
00009 11+R9 EQU 9 01-PROGR
0000A 12+R10 EQU 10 01-PROGR
0000B 13+R11 EQU 11 01-PROGR
0000B 14+BASEREG EQU 11 01-PROGR
0000C 15+R12 EQU 12 01-PROGR
0000D 16+R13 EQU 13 01-PROGR
0000E 17+R14 EQU 14 01-PROGR
0000F 18+R15 EQU 15 01-PROGR
|
STORAGE
19 PROGRAM STORAGE 00020000
00000 00012 20+STORAGE DSECT 01-PROGR
21 HW DS 0F 00020100
000000 22 HWLEN DS H 00020200
000002 23 HWTEXT DS CL16 00021000
24 PROGRAM STORAGE 00030000
25+STORAGE_LEN EQU *-STORAGE 01-PROGR
|
START
26 PGM3 PROGRAM START 00040000
00000 00048 27+SA DSECT 01-PROGR
000000 28+SAVEAREA DS 18F SAVE AREA 01-PROGR
00048 29+SA_LEN EQU *-SA 01-PROGR
00000 00188 30+PGM3 CSECT 01-PROGR
31+PGM3 AMODE 31 01-PROGR
32+PGM3 RMODE ANY 01-PROGR
33+ ENTRY PGM3 01-PROGR
and a lot more ….
|
The macro will
- Define the savearea DSECT
- Save registers in callers savearea
- Getmain a new savearea for this program
- set the base register (BASEREG)
- Getmain the storage for this programs variables/fields
- and set its base register
END
131 RETURN PROGRAM END 00080002
132+*************************
133+*
134+*************************
000120 135+RETURN DS 0H 01-PROGR
4120 0012 136+ LA R2,STORAGE_LEN 01-PROGR
000124 140+ CNOP 0,4 02-FREEM
141+ B *+12-4*0-2*0 BRANCH AROUND DATA ØO2C 02-FREEM
and a lot more ….
|
Freemains areas previous getmained by the macro and returns with cond code (default zero).
Bonus info
The macro above calls the IBM-provided macro FREEMAIN but the assembler does not write the called FREEMAIN macro out in the assembler list. I don’t know why but it writes the calling level and first five characters of the called macro in the very right of the line. That can be useful in a debug situation.
Macro Code
You do not need to read the next if you do not want to know how the macro works. If you will just use it as any other IBM provided macro you just clip the examples below and paste them one after the other into one PROGRAM macro.
Initial code
MACRO
&LABEL PROGRAM &STATE,&BASEREG=BASEREG, &STORAGEREG=R12,&SAVEAREA=SAVEAREA,&SADSECT=SA,
&RETURNCODE=0
.*************************************************************** .** DEFINE THIS MACROS GLOBAL VARIABLES .*************************************************************** GBLC &GBLC_SAVEAREA,&GBLC_SADSECT,&GBLC_BASEREG GBLC &GBLC_LABEL,&GBLC_STORAGEREG GBLB &STORAGE_DEFINED,&TRUE,&FALSE &TRUE SETB 1 &FALSE SETB 0 .*************************************************************** .** DETERMINE WHICH SECTION TO EXECUTE .*************************************************************** AIF ('&STATE' EQ 'START').START AIF ('&STATE' EQ 'END').END AIF ('&STATE' EQ 'EQU').EQU AIF ('&STATE' EQ 'STORAGE').STORAGE MNOTE 8,'FIRST PARAMETER WRONG.' AGO .MACROEND |
We need several Global Literals in this macro so I define them first. That is "best practice" to do it at the top of the macro. I use a boolean test so I set &TRUE and &FALSE. That makes the boolean test and set more readable.
I then test for each state and go to each section accordingly. You will of course get a cond code of eight and an error text if you do not obey the rules of the macro.
EQU
The EQU will create the EQU for the general registers
.EQU ANOP
R0 EQU 0
R1 EQU 1
R2 EQU 2
R3 EQU 3
R4 EQU 4
R5 EQU 5
R7 EQU 7
R8 EQU 8
R9 EQU 9
R10 EQU 10
R11 EQU 11
BASEREG EQU 11
R12 EQU 12
R13 EQU 13
R14 EQU 14
R15 EQU 15
AGO .MACROEND
EQU
The EQU will create the EQU for the general registers
.EQU ANOP
R0 EQU 0 R1 EQU 1 R2 EQU 2 R3 EQU 3 R4 EQU 4 R5 EQU 5 R7 EQU 7 R8 EQU 8 R9 EQU 9 R10 EQU 10 R11 EQU 11 BASEREG EQU 11 R12 EQU 12 R13 EQU 13 R14 EQU 14 R15 EQU 15 AGO .MACROEND |
Storage
This section is divided into two; one before storage definition and one after.
.STORAGE ANOP
AIF (&STORAGE_DEFINED).STODEF AIF ('&LABEL' EQ '').NO_LABEL &GBLC_LABEL SETC '&LABEL' AGO .DEFINE .NO_LABEL ANOP &GBLC_LABEL SETC 'STORAGE' .****************************** .DEFINE ANOP &GBLC_STORAGEREG SETC '&STORAGEREG' &GBLC_LABEL DSECT &STORAGE_DEFINED SETB (&TRUE) AGO .MACROEND
.******************************
.STODEF ANOP &GBLC_LABEL._LEN EQU *-&GBLC_LABEL AGO .MACROEND |
- First we check which section of STORAGE we will enter by checking the boolean &STORAGE_DEFINED.
- In the first section we then check to see whether the &LABEL is set. That is used to give name to the DSECT we are about to define. If it is not defined we use the default 'STORAGE'.
- We can now start defining the DSECT (in bold) and its base register which is in &STORAGEREG. At last we set the boolean (&STORAGE_DEFINED) to &TRUE to indicate that the first definition of storage has been done. (Please note the SETBinary)
- The second section just calculates the length of the storage. That will be used during getmain of the storage. We never need to know how big our storage is (*2).
START
.START ANOP
&GBLC_SAVEAREA SETC '&SAVEAREA'
&GBLC_SADSECT SETC '&SADSECT'
&GBLC_STORAGEREG SETC '&STORAGEREG'
.**************************
.** DSECT FOR THE REGISTER
.** SAVE AREA
.**************************
&GBLC_SADSECT DSECT
&GBLC_SAVEAREA DS 18F SAVE AREA
&GBLC_SADSECT._LEN EQU *-&GBLC_SADSECT
.**************************
.** START THE CSECT
.**************************
&LABEL CSECT
&LABEL AMODE 31
&LABEL RMODE ANY
ENTRY &LABEL
SAVE (14,12),,'&LABEL - &SYSDATE..&SYSTIME'
BASR &BASEREG,0
USING *,&BASEREG
LR 2,1 SAVE THE PARM REG FOR LATER USE
WTO '&LABEL - &SYSDATE.-&SYSTIME'
RSA GETMAIN RC,LV=&GBLC_SADSECT._LEN
ST 1,8(,13)
ST 13,4(,1)
LR 13,1
ST 2,0(,13)
USING &GBLC_SADSECT,13
AIF (NOT &STORAGE_DEFINED).STARTEND
GETMAIN RC,LV=&GBLC_LABEL._LEN
LR &GBLC_STORAGEREG,1
USING &GBLC_LABEL,&GBLC_STORAGEREG
.STARTEND ANOP
L 1,0(,13)
AGO .MACROEND
|
- I start by setting the GBLC’s from the input parameters to the macro
- and then defines the DSECT for the savearea.
- The real program starts with a CSECT, AMODE, RMODE, and ENTRY.
- We save the calling program’s registers
- Load the base register and set the USING register. Probably 11.
- Then I save the pointer to the parm (R1) in R2 in order to save it for future use.
- Then I write the date of assembling out with WTO. Very nice if the program is not used a million times. You know what I mean.
- Now it’s time to get some storage for the save area. Try to figure out what the four instruction between GETMAIN and USING are doing. I will probably come back to that in a later blog post.
- If we also have used the STORAGE state of PROGRAM we will also get some storage for that and set the base register for that storage (Default R12).
- (Future use): The last instruction sets the register 1 to point to the parm as it does at start in any assembler program.
END
.END ANOP
AIF ('&LABEL' EQ '').NO_LABEL
&LABEL DS 0H
AGO .RETURN
.NOLABEL ANOP
RETURN DS 0H
.RETURN ANOP
AIF (NOT &STORAGE_DEFINED).NO_STORAGE_DEFINED
LA R2,&GBLC_LABEL._LEN
FREEMAIN RC,LV=&GBLC_LABEL._LEN,A=(&GBLC_STORAGEREG)
.NO_STORAGE_DEFINED ANOP
L R13,&GBLC_SAVEAREA+4
LA R12,&GBLC_SAVEAREA
LA R2,&GBLC_SADSECT._LEN
FREEMAIN RC,LV=&GBLC_SADSECT._LEN,A=(R12)
LM R14,R12,12(R13)
RETURN RC=&RETURNCODE
LTORG
AGO .MACROEND
|
- If the END state has a label we use that label so we can branch to it. You might wish to have more than one END with different RETURNCODEs. Each of these generates all the code here and one could argue that is waste. Yes, - feel free to change the macro to solve it.
- If we define the storage and getmain’ed it we will of course freemain it again. Every good boy and girl clear up in storage after themselves!!
- That goes for the save area as well.
- We return to the caller with a return code.
- And finally, we define literals.
We conclude the macro with these two lines.
.MACROEND ANOP
MEND
|
(*1) - PROGRAM will NOT make you assembler program reentable.
(*2) - I will talk about base register limitations later. Let us assume that 4096 bytes are enough
Ingen kommentarer:
Send en kommentar