søndag den 3. januar 2016

Unix from assembler (2) - Read a Unix File

Read a Unix File

Files on Unix and z/OS-datasets have complete different design paradigms. Nearly all files on z/OS have records. You read and write records. However, on Unix, you read and write bytes and/or a stream of bytes. When you wish to read, you say to the system how many bytes you want and you will get them until there are no more bytes in the file. Unix has a kind of records though, but they are merely bytes separated by Newline (NL - X’15’).

Unix files on z/OS

When you browse a file on zFS it looks exactly as records on a dataset with the exception that they have variable length. That is because each “record” is suffixed by X’15’ (New Line) and ISPF Browse shows it as a record. The NL is not displayed
********************************* Top of Data ***
------------------------------------------------
        MACRO                                   
444444444DCCDD                                   
00000000041396                                   
------------------------------------------------
&LABEL   USSSERV &SERVICE,&PARMS                 
5DCCCD444EEEECDE45ECDECCC65DCDDE                 
0312530004222595002595935B071942                 
------------------------------------------------
        GBLC  &PARM_LABEL                       
444444444CCDC445DCDD6DCCCD                       
00000000072330007194D31253                       
------------------------------------------------
        AIF   ('&SERVICE' EQ 'PARM').PARM       
444444444CCC444475ECDECCC74CD47DCDD754DCDD       
000000000196000DD02595935D0580D7194DDB7194       
Display of Unix file on zFS. (ISPF Browse)

If you enter the same file in edit mode, you see all lines padded with spaces but that is just for you to be able to type in characters after the last character. When you save the file an NL will be placed right after the last character and the spaces after NL will be removed.
****** ***************************** Top of Data *******
000001          MACRO                                   
      444444444DCCDD44444444444444444444444444444444444
      0000000004139600000000000000000000000000000000000
--------------------------------------------------------
000002 &LABEL   USSSERV &SERVICE,&PARMS                 
      5DCCCD444EEEECDE45ECDECCC65DCDDE44444444444444444
      0312530004222595002595935B07194200000000000000000
--------------------------------------------------------
Same file in edit mode

Assembler program to read a Unix file.

I have made a program that reads a file and write the “records” to operator (WTO). Let’s go through the sections in
the program. I use my macro USSSERV to do the more tedious work of calling the USS services. You can find it in the previous article

General storage fields and instructions

PARM     USSSERV PARM,10  
RETVAL   DS   F
RETCODE  DS   F
RSNCODE  DS   F
Definition of storage for all calls.

PARM as parameter list with 10 fullwords followed by three fullwords to return values from the callable service.

You must check for any error after each call.
        ICM   R15,B'1111',RETVAL  Test RETVAL
        BL    ERROR               Branch if negative
* (-1 = failure)

OPEN - BPX1OPN

First of all you must open the file you wish to read. The open will establish a connection between your program and the file. This specific connection is “pointed” to by the result of the open in the fullword FILEDESC which must be used in each of the following commands to the file, - here READ and CLOSE.
FILEDESC DS   F    Result field that contains a “pointer” to the file.
BUFLENA  DS   F    Length of Path and name of file
BUFFERA  DS   CL13 Path and name of file
FLAGS    DS   F    Open option. Read only (O_RDONLY)      
MODE     DS   F    Is here zero
Storage fields used by Open

Open the file for input (read) and save the file descriptor
        MVC   BUFFERA(14),=CL14'/u/jee/ussserv'
        MVC   BUFLENA,=F'14'
        XC    S_MODE,S_MODE
        XC    O_FLAGS(OPNF_LENGTH),O_FLAGS
        MVI   O_FLAGS4,O_RDONLY          Read only

        USSSERV OPEN,(BUFLENA,BUFFERA,FLAGS,MODE,
              RETVAL,
              RETCODE,
              RSNCODE)

        ICM   R15,B'1111',RETVAL  Test RETVAL
        BL    ERROR               Branch if negative
* (-1 = failure)
        ST    R15,FILEDESC        Store the file descriptor
Call open for read of “/u/jee/ussserv”. The result (File descriptor) is in register 15.

Remember, that path and file names are case sensitive.

READ - BPX1RED

You can now issue READ as many times you like until End-Of-File. The READ-command takes some fields.
READ_COUNT     DS F      Saves the result from R15 of bytes read
BUFFER         DS CL40   The area with the read bytes
BUFFER_ADR     DS F      The address of the area above
BUFFER_LENGTH  DS F      The length of the buffer area
B_ALET         DS F      Not used. Must be zero
READ fields

Before we call the READ service we must initiate some of the fields. The buffer (BUFFER) must be filled with binary zeroes in case we do not get all the bytes into the buffer. This will typically be at the end of the file.

Please note, that we supply the ADDRESS of the buffer rather than the buffer itself. It might be because we could issue another read right after adding the number of bytes read to the buffer address.  That would be convenient if the buffer is greater than the number we supply in the BUFFER_LENGTH. We do not do it here!
        LA    R4,WTOTEXT
LOOP_READ DS   0H
        MVC   BUFFER_LENGTH,=A(L'BUFFER)
        XC    B_ALET,B_ALET
        XC    BUFFER,BUFFER
        LA    1,BUFFER
        ST    1,BUFFER_ADR
                                                                   
        USSSERV READ,(FILEDESC,
              BUFFER_ADR,
              B_ALET,
              BUFFER_LENGTH,
              RETVAL,
              RETCODE,
              RSNCODE)
                                                                   
        ICM   R15,B'1111',RETVAL  Test RETVAL
        BM    ERROR               Branch if negative
* (-1 = failure)
        BZ    CLOSE               End-Of-File
        ST    R15,READ_COUNT      Store the number of bytes read
READ code

We check for any errors after the read and saves the number of bytes read in READ_COUNT.

Splitting a buffer into records

We must have a routine to convert the stream input to the record output.

WTO      DS    0F  
WTOLGD   DS    H   
WTOTEXT  DS    CL80
WTO fields

This routine takes the buffer and separates it into records that can be written to operator (WTO). One buffer can contain several records and a record can be split into two buffers. The routine checks for NL (X’15’) and when it is found it writes the record. However, when the routine comes to the end of the buffer without encountering an NL it goes to read a new buffer full of bytes.
        L     R6,READ_COUNT       
        LA    R2,BUFFER           
NEXT_BYTE DS   0H                  
        CLI   0(R2),X'15'         
        BE    WRITE_BYTES         
        MVC   0(1,R4),0(R2)       
        LA    R2,1(,R2)           
        LA    R4,1(,R4)           
        BCT   R6,NEXT_BYTE        
        B     LOOP_READ           
                                  
WRITE_BYTES DS 0H                  
        LA    R2,1(,R2)           
        MVC   WTOLGD,=Y(L'WTOTEXT)
        WTO   TEXT=WTO,ROUTCDE=11
        MVI   WTOTEXT,C' '        
        MVC   WTOTEXT+1(l'WTOTEXT-1),WTOTEXT  
        LA    R4,WTOTEXT                      
        BCT   R6,NEXT_BYTE                    
        B     LOOP_READ                       

Use of general registers:
R2 Points to the byte in the input buffer
R4 Points to the byte in the output buffer (WTOTEXT)
R6 Counts down the number of bytes left in the input buffer.

The number of bytes in the input buffer is limited to the input buffer length set on the READ command (BUFFER_LENGTH)


Close BPX1CLO

Nothing special with the CLOSE. It takes the FILEDESC and disconnects the program from the file.

CLOSE    DS    0h

        USSSERV CLOSE,(FILEDESC,
              RETVAL,
              RETCODE,
              RSNCODE)
                                                                   
        ICM   R15,B'1111',RETVAL  Test RETVAL
        BM    ERROR       Branch if negative (-1 = failure)
        BZ    RETURN


Manuals

UNIX System Services Programming: Assembler Callable Services Reference (SA22-7803-14)
EBCDIC Code Page: https://en.wikipedia.org/wiki/EBCDIC_037

tirsdag den 29. december 2015

Unix from assembler (1) - Introduction

Introduction to Unix Callable Services

I start a small series of articles regarding Unix System Services (USS) and how to access Unix properties from an assembler program, either started from JCL or in the Unix shell. I start by showing how to access services and later the File System (zFS).


If you have no idea what I am talking about, please read the article linked to below. Even if you know something about USS you might benefit from reading it:

Crossing the border

Programming between native z/OS and zOS UNIX Systems Services
I have borrowed some of the examples in the article.

Calling the services

In the Unix society the programming language C is what assembler is for z/OS. So it can be no surprise that z/OS offers all services in Unix C to assembler. These services are the same modules that is used by C. There are (at least) three ways to find these Callable Services:
  1. by static linking each module into your load module and CALL it
  2. by loading the module into storage and CALL it
  3. by finding the address of the preloaded module in a control block and CALL it
I will definitely recommend the latter and that is what my examples will do.
       L     R15,16              R15 -> Common Vector Table
       L     R15,CVTCSRT-CVT(15) R15 -> CSRTABLE
       L     R15,24(R15)         R15 -> CSR slot
       L     R15,276(R15)        R15 = Address of service svc
       BALR  14,15                                               
There is a table of addresses of each module. At offset 276 is the address of “getpid” (get process id).

Parameters

You pass parameters in a list of addresses to the module exactly as you would to any other module.
PARM      DS   F
PROCESSID DS   F
        ...
        ...
        LA    R15,PROCESSID
        ST    R15,PARM     
        OI    PARM,X'80'
        LA    1,PARM    
This example passes only one parameter, PROCESSID but we will later handle several parameters

MACRO to USS Callable Service
I am a macro guy so I have of course made a macro to facilitate the call of a Unix Callable Service. It is supposed to be extended with offsets to more Unix Callable Services.

        MACRO                                      
.***************************************************
.**                                                 
.** Macro USSSERV - Call USS Callable Services    **
.**                                                 
.***************************************************
&LABEL   USSSERV &SERVICE,&PARMS                    
        GBLC  &PARM_LABEL                          
.***************************************************
.** Storage definitions                           **
.***************************************************
        AIF   ('&SERVICE' EQ 'PARM').PARM          
        AIF   ('&SERVICE' EQ 'DSECT').DSECT        
.***************************************************
.** Callable Services                             **
.***************************************************
        AIF   ('&SERVICE' EQ 'GETPID').GETPID      
        AIF   ('&SERVICE' EQ 'OPEN').OPEN          
        AIF   ('&SERVICE' EQ 'CLOSE').CLOSE        
        AIF   ('&SERVICE' EQ 'READ').READ          
        MNOTE 8,'Wrong SERVICE'                    
        AGO   .END                                 
.***************************************************
.** Define parm list                              **
.***************************************************
.PARM    ANOP                                       
&LABEL   DS    &PARMS.F                             
&PARM_LABEL SETC '&LABEL'                           
&PARM_LABEL._LENGTH EQU *-&LABEL                    
        AGO   .END                                 
.***************************************************
.** Define DSECT for the BPX-services             **
.***************************************************
.DSECT ANOP                                         
        PUSH  PRINT                                
        PRINT NOGEN                                
        CVT   DSECT=YES                            
        BPXYMODE LIST=NO                           
        BPXYOPNF LIST=NO                           
        POP   PRINT                                
        AGO   .END                                 
.***************************************************
.** Get Process id                                **
.***************************************************
.GETPID  ANOP                                       
&OFFSET  SETC  '276'                                
        AGO   .MAKECODE                            
.***************************************************
.** Open file                                     **
.***************************************************
.OPEN    ANOP                                       
&OFFSET  SETC  '156' BPX1OPN                        
        AGO   .MAKECODE                            
.***************************************************
.** Read a file                                   **
.***************************************************
.READ    ANOP                                       
&OFFSET  SETC  '176' BPX1RED                        
        AGO   .MAKECODE                            
.***************************************************
.** Close a file                                  **
.***************************************************
.CLOSE   ANOP                                       
&OFFSET  SETC  '72'  BPX1CLO                        
        AGO   .MAKECODE                            
.***************************************************
.** Make the code to set the parm and the address **
.** of the Service                                **
.***************************************************
.MAKECODE ANOP                                      
&N       SETA  N'&PARMS                             
&I       SETA  0                                    
.LOOP_PARMS ANOP                                    
        LA    R15,&PARMS(&I+1)                     
        ST    R15,&PARM_LABEL+(4*&I)
&I       SETA  &I+1
        AIF   (&I LT &N).LOOP_PARMS
        OI    &PARM_LABEL+(4*(&N-1)),X'80'
.**************************************
.** Find the Callable Service        **
.************************************** 
        L     15,16              R15 -> Common Vector Table
        L     15,CVTCSRT-CVT(15) R15 -> CSRTABLE
        L     15,24(R15)         R15 -> CSR slot
        L     15,&OFFSET.(15)    R15 = Address of service svc
        LA    1,&PARM_LABEL 
        BALR  14,15 .***************************************************
.** End-Of-Macro                                  **
.***************************************************
.END     ANOP
        MEND

The macro has a “Service” called “CVTDSECT”. That must be placed outside any DSECT or CSECT. It is used to calculate an offset.


I will change this macro as I need more services and parameters.

Exampel program
        PROGRAM EQU                     
*************************                
WS       PROGRAM STORAGE                 
WSEC     DS    CL16                      
PARM     DS    f                         
PROCESSID DS   F                         
PID_PACKED DS  PL8                       
PID_CHAR DS    CL16      
WTO      DS    0f                        
WTOLGD   DS    H                         
WTOTEXT  DS    CL70                      
        PROGRAM STORAGE                 
        USSSERV CSVDSECT                
*************************                
PGM12    PROGRAM START                   
        MVC   WSEC,=CL(L'WSEC)'PGM12-WS'
        USSSERV GETPID,PROCESSID        
        l     R1,processid              
        CVD   R1,PID_PACKED             
        MVC   PID_CHAR,EDIT_FIELD                 
        ED    PID_CHAR,PID_PACKED                 
        MVC   WTOTEXT(10),=C'Processid:'          
        MVC   WTOTEXT+10(l'PID_CHAR),PID_CHAR     
        MVC   WTOLGD,=Y(l'WTOTEXT)                
        WTO   TEXT=WTO,ROUTCDE=11                 
        B     RETURN                              
EDIT_FIELD DC  C' ',(L'PID_CHAR-1)X'20'            
NO_STORAGE_OBTAINED DS 0H                          
        WTO   'PGM12 - Return med fejl',ROUTCDE=11
        L     R13,SAVEAREA+4                      
        LM    R14,R12,12(R13)                     
        RETURN RC=8                               
*************************                          
* End of program
*************************                          
RETURN   PROGRAM END                               
        LTORG                                     
        END

Manual

z/OS V1R13.0 UNIX System Services Programming: 
Assembler Callable Services Reference - SA22-7803-14
List of offsets on page 1011 (Table 24. System control offsets to callable services)