The @PRINT macro will allow us to print a single line to a printer. By default the printer is assumed to be a the standard address x’00E’. There are two forms of this macro – GEN=YES and GEN=NO. GEN=NO is the default form and is used to generate a call to our subroutine to print a line. The GEN=YES form is used to generate the code for our print subroutine.
MACRO
&NAME @PRINT &DATA,&LEN=132,&DEV=X'00E',&GEN=NO
LCLC &NDX
LCLA &K
&NDX SETC '&SYSNDX'
AIF ('&GEN' EQ 'YES').GEN
We being by defining a couple of macro local variables – &NDX and &K. We set &NDX to the value contained in the assembler &SYSNDX which will contain a unique number for each macro invocation. We will use this to generate unique lable values in our macros. Next we test to see if GEN=YES has been specified and if so we branch to generate the subroutine code.
AIF ('&DATA'(1,1) EQ '''').STRING
&NAME @RGCK &DATA,REG=R1
@RGCK &LEN,REG=R0
LA R15,@PRINT
BALR R14,R15
MEXIT ,
Next we check to see if our &DATA parameter is a string. We check this before using the @RGCK macro because we want to use the length of the string in our macro. If not a string then we load the data target address into R1 and the data length into R0 (using a length of 132 as a default). We then BALR to our print subroutine.
.STRING ANOP ,
&NAME LA R1,=C&DATA
&K SETA K'&DATA
&K SETA &K-2
LA R0,&K
LA R15,@PRINT
BALR R14,R15
MEXIT ,
Here we generate a LA to place the address of a literal string into R1. We set &K to the length of the string operand and subtract two (for the beginning and ending quotes) to get the data length which we place in R0. We then call the print subroutine.
We will use a standard OS save area and linkage. We could use any other approach since we are not interfacing with the operating system – we are the operating system! We will always expect R13 to contain the address of a 18 word save area.
.GEN ANOP ,
@PRINT STM R14,R12,12(R13) SAVE CALLERS REGISTERS
LR R2,R1 POINT TO DATA TO PRINT
LR R3,R0 LENGTH OF DATA TO PRINT
LA R1,132 MAX LENGTH WE CAN PRINT
CR R3,R1 TOO BIG
BNH @&NDX.01 NO - BRANCH
LR R3,R1 SET TO MAX
@&NDX.01 DS 0H
Now we copy the data address into R2 although we could have left it in R1 but out of habit I copied it freeing up R1. We then copy the length from R0 into R3 and compare it to the max length of 132. If the length is larger than the max we set it to the max value.
ST R2,@&NDX.90 SAVE DATA ADDR INTO CCW
MVI @&NDX.90,X'09' PRINT: SINGLE SPACE
ST R3,@&NDX.90+4 SAVE LENGTH INTO CCW
LA R1,@&NDX.90 POINT TO CCW
ST R1,72 SAVE INTO CAW
LA R2,&DEV GET PRINT DEVICE ADDRESS
SIO 0(R2)
BNZ @&NDX.81 BRANCH IF ERROR
Next we place the data address into the CCW. We then set our CCW Op Code to X’09’ to print and single space followed by the length of the data. We then get the address of our CCW and store it into the Channel Address Word at fixed location 72. We can then issue a SIO to the printer using the device address from our macro parameter &DEV. We check to verify the SIO was accepted and if not we branch to an error routine to load a wait state PSW.
@&NDX.02 DS 0H
TIO 0(R2) WAIT FOR I/O TO COMPLETE
BZ @&NDX.03 COMPLETE
BC 1,@&NDX.82 BRANCH IF ERROR
B @&NDX.02 LOOP BACK AND WAIT
*
@&NDX.03 DS 0H
LM R14,R12,12(R13) RESTORE REGISTERS
BR R14
A TIO loop is executed until the I/O is complete and then we can restore the registers and return to the caller.
@&NDX.81 LPSW @ERRPRT1 LOAD WAIT PSW
@&NDX.82 LPSW @ERRPRT2 LOAD WAIT PSW
DS 0D
@&NDX.90 DC X'00',AL3(0),X'00',X'00',AL2(0) CCW
MEND ,
Now all we have left to is it define our error routines to load a wait state PSW. Our error PSW definitions are in our @ERR macro. We also need to allocate the space where we will construct our CCW.
Now we can print to the printer using our macro.
@PRINT 'HELLO, WORLD' * Print Hello Message
@PRINT ' ' * Print a Blank Line
@PRINT MSG,LEN=9 * Print string 8 chars long
MSG DC C'HI, WORLD'
@PRINT (AMSG) * Print 132 char string
AMSG DC A(OURMSG)
OURMSG DC CL132'*** THIS IS OUR MESSAGE ***'
LA R10,OURMSG * Print 132 char string
@PRINT (R10)
Full macro listing is below.
MACRO
&NAME @PRINT &DATA,&LEN=132,&DEV=X'00E',&GEN=NO
LCLC &NDX
LCLA &K
&NDX SETC '&SYSNDX'
AIF ('&GEN' EQ 'YES').GEN
AIF ('&DATA'(1,1) EQ '''').STRING
&NAME @RGCK &DATA,REG=R1
@RGCK &LEN,REG=R0
LA R15,@PRINT
BALR R14,R15
MEXIT ,
.*
.*
.STRING ANOP ,
&NAME LA R1,=C&DATA
&K SETA K'&DATA
&K SETA &K-2
LA R0,&K
LA R15,@PRINT
BALR R14,R15
MEXIT ,
.*
.*
.GEN ANOP ,
@PRINT STM R14,R12,12(R13) SAVE CALLERS REGISTERS
LR R2,R1 POINT TO DATA TO PRINT
LR R3,R0 LENGTH OF DATA TO PRINT
LA R1,132 MAX LENGTH WE CAN PRINT
CR R3,R1 TOO BIG
BNH @&NDX.01 NO - BRANCH
LR R3,R1 SET TO MAX
@&NDX.01 DS 0H
ST R2,@&NDX.90 SAVE DATA ADDR INTO CCW
MVI @&NDX.90,X'09' PRINT: SINGLE SPACE
ST R3,@&NDX.90+4 SAVE LENGTH INTO CCW
LA R1,@&NDX.90 POINT TO CCW
ST R1,72 SAVE INTO CAW
LA R2,&DEV GET PRINT DEVICE ADDRESS
SIO 0(R2)
BNZ @&NDX.81 BRANCH IF ERROR
@&NDX.02 DS 0H
TIO 0(R2) WAIT FOR I/O TO COMPLETE
BZ @&NDX.03 COMPLETE
BC 1,@&NDX.82 BRANCH IF ERROR
B @&NDX.02 LOOP BACK AND WAIT
*
@&NDX.03 DS 0H
LM R14,R12,12(R13) RESTORE REGISTERS
BR R14
*
@&NDX.81 LPSW @ERRPRT1 LOAD WAIT PSW
@&NDX.82 LPSW @ERRPRT2 LOAD WAIT PSW
DS 0D
@&NDX.90 DC X'00',AL3(0),X'00',X'00',AL2(0) CCW
MEND ,