I needed to use some assembler macros inside a C program, because there was no native C interface. This was to prompt the operator for a password, but not to display the value which was entered.
This “simple program” took a few hours to get working.
I’ve written Re-entrant assembler macros in z/OS explaining how to use assembler macros in re-entrant code.
I needed to do this from with a C program using the ASM() definition to write assembler code.
Set up the C code
/*Include standard libraries */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( int argc, char *argv[])
{
struct{
short ll; // length of the message
char text[120]; // the message itself
} outputMsg; // the displayed message
int tempWto[100] ; // plenty of space, on a full word boundary
int ECB = 0; // wait on this
// Define the reply area
int lReply = 100;
// use +1 to define a trailing null
char reply[lReply+1];
memset(&reply[0],0,lReply+1); // set to nulls
int rc = 0;
char * outMsg = "Please give the password for userid ABC";
strncpy(&outputMsg.text[0],outMsg,sizeof(outputMsg.text));
outputMsg.ll = strlen(outMsg);
The WTOR macros
The macros I needed to use were the Write To Operator with Reply(WTOR).
The code needs to
- define the static data for the WTOR
- copy the WTOR static structure data to thread read-write storage
- execute the WTOR passing the parameters, and the WTOR structure in read-write storage. The parameters are
- the message to display
- the address of the reply buffer
- the length of the reply buffer
- the ECB to wait on
Define the static data
asm( "WTORL WTOR TEXT=(,,,),MF=L,ROUTCDE=(9) \n"
"OVERWTO DS 0H \n"
The ROUTCDE=(9) says supress what was typed in. This WTOR is being used to prompt for a password, using ROUTCDE=(9) achieves this.
Execute the WTOR
asm(...
" LA 2,%[out] \n"
" WTOR TEXT=((2),(%[pReply]),(%[rLen]),%[ECB]),MF=(E,%[pData]) \n"
" ST 15,%[rc] \n"
" LTR 15,15 \n"
" JNZ ERROR \n"
" WAIT 1,ECB=%[ECB] \n"
"ERROR DS 0H \n"
: [rc] "+m"(rc), //* output
[rLen] "+r"(lReply)
: [out] "m"(outputMsg),
[pReply] "r"(&reply),
[ECB] "m"(ECB),
[pData] "m"(tempWto[0])
:"r0", "r1" , "r15", "r2" );
The LA 2,%[out] code uses the definition [out] further down. The “m” says use memory, and substitute the address of outputMsg. In the C program this is at address 168 off register 13.
The LA 2,%[out] becomes
LA 2,168(13)
The WTOR TEXT=((2),(%[pReply]),(%[rLen]),%[ECB]),MF=(E,%[pData]) statement becomes
WTOR TEXT=((2),(5),(4),696(13)),MF=(E,296(13))
- [pReply] was defined as a temporary register “r”. It is surrounded by () to show that it is a register
- [rLen] was defined as a temporary register “r”. It is surrounded by () to show that it is a register
- [ECB] is defined as a memory location, and it’s address 696 off register 13 is used
- the read write storage to use is at offset 296 off register 13
It was hard to know which fields had to be passed in registers, and which could be passed as memory addresses. I solved it by trial and error.
The hard part
The statically defined structure needs to be copied to thread read write storage. This proved a challenge.
In assembler you can use an instruction like MVC TO(24),FROM and it copies 24 bytes from FROM to TO. Using the assembler from C means you cannot use this.
- There are no base+using registers, so you cannot reference a field by a label
- You cannot specify a length field when using %[name].
I used the MVCL which allows registers to be used to address the data, and specify the length. You need two registers to identify the “from” area, and two registers to identify the “to” area.
asm(" BRAS 14,OVERWTO \n" // point R14 to the constant area
"WTORL WTOR TEXT=(,,,),MF=L,ROUTCDE=(9) \n"
"OVERWTO DS 0H \n"
" LA 15,OVERWTO-WTORL \n"
" LR 1,15 \n" // make the lengths the same
" LA 0,%[pData] \n" // where the data is stored
" MVCL 0,14 \n" // move from the static to the dynamic
Where
- BRAS 14,OVERWTO sets register 14 to the address of the data following, and jumps to the label OVERWTO
- LA 15,OVERWTO-WTORL gets the length of the statically defined data
- LR 1,15 copies the length of the data into register 1
- LA 0,%[pData] points register 0 to the address of the thread read-write storage
- MVCL 0,14 This copies the data from what register 14 points to (the static data) with a length of the content of register 15, into the storage pointed to by register 0, of length in the contents of register 1
Wait for the reply
The code was
" WTOR TEXT=((2),(%[pReply]),(%[rLen]),%[ECB]),MF=(E,%[pData]) \n"
" ST 15,%[rc] \n"
" LTR 15,15 \n"
" JNZ ERROR \n"
" WAIT 1,ECB=%[ECB] \n"
"ERROR DS 0H \n"
The code
- checks the return code from the WTOR was zero
- If not, skip the ECB wait
- Wait for one ECB posted, with the specified ECB
The after code
printf("Return code %i\n",rc);
printf("Data %i %s\n",strlen(reply),reply);
return rc;
}
This prints what the user entered. Because the reply buffer was primed with hex 00, you can use STRLEN to get the length of the returned string.
The the program ran, it returned data in upper case. I had to reply to the WTOR on the console using R nn,’lower case’.
The whole program
/*Include standard libraries */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( int argc, char *argv[])
{
// the output message
struct{
short ll;
char text[120];
} outputMsg;
int tempWto[100] ; // plenty of space, on a full word boundary
int ECB = 0;
int lReply = 100;
// use +1 to define a trailing null
char reply[lReply+1];
memset(&reply[0],0,lReply+1); // set to nulls
int rc = 0;
char * outMsg = "Please give the password for userid ABC";
strncpy(&outputMsg.text[0],outMsg,sizeof(outputMsg.text));
outputMsg.ll = strlen(outMsg);
asm(" BRAS 14,OVERWTO \n" // point R14 to the constant area
"WTORL WTOR TEXT=(,,,),MF=L,ROUTCDE=(9) \n"
"OVERWTO DS 0H \n"
" LA 15,OVERWTO-WTORL \n"
" LR 1,15 \n" // make the lengths the same
" LA 0,%[pData] \n"
" MVCL 0,14 \n" // move from the static to the dynamic
" LA 2,%[out] \n"
" WTOR TEXT=((2),(%[pReply]),(%[rLen]),%[ECB]),MF=(E,%[pData]) \n"
" ST 15,%[rc] \n"
" LTR 15,15 \n"
" JNZ ERROR \n"
" WAIT 1,ECB=%[ECB] \n"
"ERROR DS 0H \n"
: [rc] "+m"(rc) // + means modified output
: [out] "m"(outputMsg),
[pReply] "r"(&reply),
[rLen] "r"(lReply),
[ECB] "m"(ECB),
[pData] "m"(tempWto[0])
:"r0", "r1" , "r15", "r2"
);
printf("Return code %i\n",rc);
printf("Data %i %s\n",strlen(reply),reply);
return rc;
}