I wanted to provide a Python started task on z/OS to respond to the operator Stop, and Modify commands (for example to pass commands to the program).
I wrote a Python extension in C, and got the basics working. I then wanted to extend this a bit more. I learned a lot about the interface from C to assembler, and some of the newer linkage instructions.
The sections in this post are
- Information on programming the C to assembler interface
- Calling an assembler routine from a C program
- Setting up the linkage
- Allocating variable storage
- Coding the assembler routine
- Assembler Linkage
- Using 64 and 31 bit registers
- Using QEDIT to catch operator commands
- Can I use __ASM__ within C code to generate my own assembler?
Some of the problems I had were subtle, and the documentation did not cover them.
C provides a run time facility called __console2 which provide a write to the console, and a read from the console.
The output from the write to the console using __console2 looks like
BPXM023I (COLIN) from console2
Thanks to Morag who pointed out you get the BPXM023i(userid) if the userid does not have READ access to BPX.CONSOLE in class(FACILITY).
With the BPXM023I prefix, which I thought looked untidy and unnecessary.
To use the Modify operator command with __console2 you have to use a command like
F PYTTASK,APPL=’mydata’
Which feels wrong, as most of the rest of z/OS uses
F PYTTASK,mydata
This can be implemented using the QEDIT assembler interface.
The journey
I’ll break my journey into logical steps.
Information on programming the C to assembler interface
There is not a lot of good information available.
- MVS Programming: Assembler Services Guide PDF SA23-1368-50
- The infamous POP ( Principals of Operation) tells you every detail about every instruction – but not how to use it!
- z/OS MVS Programming: Extended Addressability Guide PDF SA23-1394-30
- SHARE presentation Tutorial on Trimodal Programming on z/OS
- John Ehrman’s text ‘Assembler Language Programming for IBM System z Servers V2′. Extensive book covering all aspects of programming in z/Series assembler. It has over 1000 pages!
Calling an assembler routine from a C program.
Setting up the linkage
A 64 bit program uses the C XPLINK interface between C programs.
To use the traditional Assembler interface you need to use
#pragma linkage(QEDIT , OS)
…
rc = QEDIT( pMsg, 6);
C does not set the Variable Length parameter list bit (the high bit of the last parameter to ’80…) so you cannot use parameter lists with a variable length, and expect traditional applications to work. You could always pass a count of parameters, or build the parameter list yourself.
Register 1 pointed to a block of storage, for example to parameters
00000000 203920D8 00000050 082FE3A0
which is two 64 byte addresses, the address of the pMsg data, and the address of the fullword constant 6;
The C code invokes the routine by
LG r15,=V(QEDIT)(,…,…)
BALR r14,r15
even though use of BALR is deprecated.
Allocating variable storage
The z/OS services my assembler program used, need 31 bit storage.
I allocated this in my C program using
char * __ptr32 pMsg;
pMsg = (char *) __malloc31(1024);
I then passed this to my assembler routine.
Coding the assembler routine
Assembler Linkage
The basic linkage was
BSM 14,0
BAKR 14,0…….
PR go back
This is where it started to get complicated. The BAKR… PR is a well documented and commonly used interface.
A Branch and StacK Register instruction BAKR 14,15 says branch to the address in register 15, save the value of register 14 as the return address, and save the registers and other status in the linkage stack. The code pointed to by register 15 is executed, and at the end, there is a Program Return (PR) instruction which loads registers from the linkage stack, and goes to the “return address”.
The Branch and Stack instruction BAKR 14,0 says do not branch, but save the status, and the return address. A subsequent PR instruction will go to where register 14 points to.
Unfortunately, with the BALR code in C, and the BAKR, PR does not work entirely.
You can be executing in a program with a 64 bit address instructions (such as 00000050 089790A0), in 24 or 31, or 64 bit mode.
- In 64 bit mode, all the contents of a register are used to address the data.
- In 31 bit mode only the bottom(right) half of the register are used to address the data – the top half is ignored
- In 24 bit mode, only the bottom 24 bits of the register are used to address the data.
There are various instructions which change which mode the program is executing in.
When a BAKR, or BASSM ( Branch and Save, and set Mode) is used, the return address is changed to set the mode ( 24,31,64) as part of the saved data. When this address is used as a branch back – the save mode information is used to switch back to the original mode.
When BALR (or BASR) is used to branch to a routine, the return address is saved in Register 14. The mode information is not stored. When this address is used as a branch back – the “default mode” information (24 bit) is used to set the mode. This means the return tries to execute with a 24 bit address – and it fails.
You solve this problem by using a (BRANCH AND SET MODE) BSM 14,0 instruction. The value of 0 says do not branch, so this just updates register 14 with the state information. When BAKR is issued, the correct state is saved with it.
If you use the “correct” linkage you do not need to use BSM. It is only needed because the C code is using an out dated interface. It still uses this interface for compatibility with historically compiled programs.
Note: BSM 0,14 is also a common usage. It is the standard return instruction in a program entered by means of BRANCH AND SAVE AND SET MODE (BASSM) or a BRANCH AND SAVE (BAS). It means branch to the address in register 14, and set the appropriate AMODE, but do not save in the linkage stack.
Using 64 and 31 bit registers
Having grown up with 32 bit registers, it took a little while to understand the usage 64 bit registers.
In picture terms all registers are 64 bit, but you can have a piece of paper with a hole in it which only shows the right 32 bit part of it.
When using the full 64 bit registers the instructions have a G in them.
- LR 2,3 copies the 32 bit value from register 3 into the right 32 bits of register 2
- LGR 2,3 copies the value of the 64 bit register 3 into the 64 bit register 2
If there is a block of storage at TEST with content 12345678, ABCDEFG
- R13 has 22222222 33333333
- copy R13 into Reg 7. LGR R7,R13. R7 contains 22222222 33333333
- L R7,TEST. 32 bit load of data into R7. R7 now has 22222222 12345678. This has loaded the visible “32 bit” (4 bytes) part of the register, leaving the rest unchanged.
- LG R8,TEST. 64 bit load into Reg 8. R8 now has 12345678 ABCDEFG . The 8 bytes have been loaded.
- “clear high R9” LLGTR R9,R9. R9 has 00000000 ……… See below.
- L R9,TEST . 32 bit (4 bytes) load into R9. R9 now has 00000000 12345678
Before you use any register in 32 bit code you need to clear the top.
The Load Logical Thirty One Bits (LLGTR R1,R2) instruction, takes the “right hand part” of R2 and copies it to the right hand of R1, and sets the left hand part of R1 to 0. Or to rephrase it, LLGTR R2,R2 just clears the top of the register.
Using QEDIT to catch operator commands
QEDIT is an interface which allows an application to process operator commands start, modify and stop, on the address space.For example
- S PYTASK,,,’STARTPARM’
- f PYTASK,’lower case data’
- p PYTASK
Internally the QEDIT code uses CIBs (Console Information Block) which have information about the operator action
(I think QEDIT comes from “editing”= removing the Queue of CIBs once they have been processed – so Q EDIT).
The interface provides an ECB for the application to wait on.
The documentation was ok, but could be clearer. For example the sample code is wrong.
In my case I wanted a Python thread which used console.get() to wait for the operator action and return the data. You then issue the console.get() to get the next operator action.
My program had logic like
- Use the extract macro to get the address of the CIBS.
- If this job is a started task, and it is the ‘get’ action then there will be a CIB with type=Started
- Return the data to the requester
- Remove the CIB from the chain
- Return to the requester
- Set the backlog of CIBs supported ( the number of operator requests which can be outstanding)
- WAIT on the ECB
- Return the data to the requester
- Remove the CIB from the chain
- Return to the requester
The action can be “Start”, “Modify”, or “Stop”
Can I use __ASM__ within C code to generate my own assembler?
In theory yes. The documentation is missing a lot of information. I could not get my simplest tests to work and return what I was expecting.
The output from __console2() looks like that because your user ID does not have access to BPX.CONSOLE in the SAF FACILITY class. However, I agree that calling WTO directly in __asm__ is better anyway.
LikeLike
Thank you.. I didn’t know that. It makes it tough to document, if one userid has access and another doesn’t. It means they get different output
LikeLike