Sample Program

The following sample program illustrates the process of writing an INFILE/FILE user exit. Notice that this program is not trivial. Writing user exits requires a firm understanding of register manipulation and other fairly advanced programming techniques.
The example uses z/OS services to compress data. The data is compressed on output and decompressed on input.
Note: This code is actually implemented in SAS, to support the CSRC option in the INFILE and FILE statements. The CSRC option is described in Standard Host Options for the FILE Statement under z/OS and in Standard Options for the INFILE Statement under z/OS.
The example consists of several assembly macros, followed by the assembly language program itself. The macros define how the parameter lists are to be interpreted. Each macro begins with a MACRO statement and ends with a MEND statement. The actual program begins on the line that reads SASCSRC START. Here is the example:
TITLE 'INFILE/FILE USER EXIT TO COMPRESS DATA USING ESA SERVICES'
*----------------------------------------------------------------------
* COPYRIGHT (C) 2010 BY SAS INSTITUTE INC., CARY, NC 27513 USA
*
* NAME:      ==> SASCSRC
* TYPE:      ==> EXTERNAL FILE USER EXIT
* LANGUAGE:  ==> ASM
* PURPOSE:   ==> TO COMPRESS/DECOMPRESS DATA USING CSRCESRV SERVICES
* USAGE:     ==> DATA;INFILE MYFILE CSRC;INPUT;RUN;
*----------------------------------------------------------------------
* - - - - - - - - - -
         MACRO
*---------------------------------------------------------------------
* COPYRIGHT (C) 2010 BY SAS INSTITUTE INC., CARY, NC 27513 USA
*
*  NAME      ==> VXEXIT
*  PURPOSE   ==> DSECT MAPPING OF INFILE EXIT TABLE
*---------------------------------------------------------------------
        VXEXIT
*------------------------------------------------------------------
* MAP OF USER EXIT HOST BAG
*------------------------------------------------------------------
VXEXIT   DSECT
         SPACE 1
*------------------------------------------------------------------
* THE FOLLOWING FIELDS MUST NOT BE CHANGED BY THE EXIT ROUTINE
* EXCEPT USERWORD
*------------------------------------------------------------------
EXITIDB  DS    A
EXITEP   DS    A
MEMALEN  DS    F                LENGTH OF WORK AREA ABOVE 16M LINE
MEMABV   DS    A                POINTER TO WORK AREA ABOVE 16M LINE
MEMBLEN  DS    F                LENGTH OF WORK AREA BELOW 16M LINE
MEMBEL   DS    A                POINTER TO WORK AREA BELOW 16M LINE
USERWORD DS    A    (USER UPD)  WORD AVAILABLE TO EXIT
EDDNAME  DS    CL8              LOGICAL NAME OF THE FILE
VARRTN   DS    A                SAS VARIABLE CREATING ROUTINE ADDRESS
ERRMSG   DS    A    (USER UPD)  NULL TERMINATED ERROR MESSAGE POINTER
EFLAG1   DS    XL1  (USER UPD)  FLAG BYTE-1
EX_NEXT  EQU   X'80'            GET NEXT RECORD FROM EXIT
EX_DEL   EQU   X'40'            DELETE THIS RECORD
EX_EOF   EQU   X'20'            EOF OF DATASET REACHED
EX_EOFC  EQU   X'10'            CALL USER EXIT AFTER EOF
EX_ALC   EQU   X'08'            WILL USE ALLOC/FREE ROUTINES
EX_STOR  EQU   X'04'            WILL SUPPORT STORED PROGRAMS
EX_TERM  EQU   X'02'            WILL NEED A TERMINAL CALL
EFLAG2   DS    XL1              FLAG BYTE-2
EFLAG3   DS    XL1              FLAG BYTE-3
EFLAG4   DS    XL1              FLAG BYTE-4
ALLOC    DS    A                ALLOC ROUTINE
FREE     DS    A                FREE ROUTINE
PIDA     DS    F                PID ABOVE
PIDB     DS    F                PID BELOW
ALLOC1   DS    A                ALLOCATE ROUTINE WITH SWITCH
FREE1    DS    A                FREE ROUTINE WITH SWITCH
VARRTN1  DS    A                SAS VARIABLE CREATING ROUTINE WITH SWITCH
VXCRAB   DS    A                CRAB ADDRESS
LOG      DS    A                LOG ROUTINE WITHOUT SWITCH
LOG1     DS    A                LOG ROUTINE WITH SWITCH
         SPACE 1
         DS    0D
         SPACE 1
VXEXITL  EQU   *-VXEXIT
*------------------------------------------------------------------
* MAP OF VARRTN FUNCTION CALL
*------------------------------------------------------------------
PARMVAR  DSECT
*
VARNAME  DS    A                 POINTER TO VARIABLE NAME
VARNAMEL DS    F                 VARIABLE NAME LENGTH
VARTYPE  DS    F                 VARIABLE TYPE 1=NUM, 2=CHAR
VARSIZE  DS    F                 SIZE OF VARIABLE IF CHAR
VARFLAG  DS    F                 FLAGS , X'01' - INTERNAL
*                                        X'02' - EXTERNAL
VARADDR  DS    A                 POINTER TO VAR LOC ADDRESS (RETURNED)
*
* FOR CHARACTER VARIABLE IT RETURNS A POINTER TO A STRING STRUCTURE
*
* MAXLEN DS    H                 MAX LENGTH OF STRING
* CURLEN DS    H                 CURRENT LENGTH OF STRING
* ADDR   DS    A                 ADDRESS OF STRING DATA
PARMVARL EQU   *-PARMVAR
*------------------------------------------------------------------
* MAP OF ALLOC FUNCTION CALL
*------------------------------------------------------------------
PARMALC  DSECT
*
ALCEXIT  DS    A                 POINTER TO THE EXIT BAG
ALCPTR   DS    A                 PLACE TO RETURN ALLOCATED ADDRESS
ALCLEN   DS    F                 LENGTH OF MEMORY REQUIRED
ALCFLG   DS    F                 FLAG BYTE  1=BELOW 16M, 0=ABOVE 16M
PARMALCL EQU   *-PARMALC
*------------------------------------------------------------------
* MAP OF FREE FUNCTION CALL
*------------------------------------------------------------------
PARMFRE  DSECT
*
FREEXIT  DS    A                 POINTER TO THE EXIT BAG
FREPTR   DS    A                 ADDRESS OF FREEMAIN
FREFLG   DS    F                 FLAG BYTE  1=BELOW 16M, 0=ABOVE 16M
PARMFREL EQU   *-PARMFRE
*------------------------------------------------------------------
* MAP OF INIT EXIT CALL
*------------------------------------------------------------------
PARMINIT DSECT
*
INITFUNC DS    F                 FUNCTION CODE
INITEXIT DS    A                 USER EXIT BAG ADDRESS
INITMBLN DS    A                 PTR TO AMT OF MEMORY NEEDED BELOW LINE
INITMALN DS    A                 PTR TO AMT OF MEMORY NEEDED ABOVE LINE
PARMINIL EQU   *-PARMINIT
*------------------------------------------------------------------
* MAP OF PARSE EXIT CALL
*------------------------------------------------------------------
PARMPARS DSECT
*
PARSFUNC DS    F                 FUNCTION CODE
PARSEXIT DS    A                 USER EXIT BAG ADDRESS
PARSOPTL DS    F                 OPTION NAME LENGTH
PARSOPTN DS    A                 POINTER TO OPTION NAME
PARSVALL DS    F                 OPTION VALUE LENGTH
PARSVAL  DS    A                 OPTION VALUE
PARMPARL EQU   *-PARMPARS
*------------------------------------------------------------------
* MAP OF OPEN EXIT CALL
*------------------------------------------------------------------
PARMOPEN DSECT
*
OPENFUNC DS    F                 FUNCTION CODE
OPENEXIT DS    A                 USER EXIT BAG ADDRESS
OPENMODE DS    F                 OPEN MODE
OPENZLEN DS    A                 POINTER TO DATA LENGTH
OPENBLKL DS    F                 DATA SET BLOCK SIZE
OPENRECL DS    F                 DATA SET RECORD LENGTH
OPENRECF DS    F                 DATA SET RECORD FORMAT
PARMOPEL EQU   *-PARMOPEN
*------------------------------------------------------------------
* MAP OF READ EXIT CALL
*------------------------------------------------------------------
PARMREAD DSECT
*
READFUNC DS    F                 FUNCTION CODE
READEXIT DS    A                 USER EXIT BAG ADDRESS
READRECA DS    A                 POINTER TO RECORD AREA ADDRESS
READRECL DS    A                 POINTER TO RECORD LENGTH
PARMREAL EQU   *-PARMREAD
*------------------------------------------------------------------
* MAP OF WRITE EXIT CALL
*------------------------------------------------------------------
PARMWRIT DSECT
*
WRITFUNC DS    F                 FUNCTION CODE
WRITEXIT DS    A                 USER EXIT BAG ADDRESS
WRITRECA DS    A                 POINTER TO RECORD AREA ADDRESS
WRITRECL DS    F                 RECORD LENGTH
PARMWRIL EQU   *-PARMWRIT
*------------------------------------------------------------------
* MAP OF CLOSE EXIT CALL
*------------------------------------------------------------------
PARMCLOS DSECT
*
CLOSFUNC DS    F                 FUNCTION CODE
CLOSEXIT DS    A                 USER EXIT BAG ADDRESS
PARMCLOL EQU   *-PARMCLOS
*------------------------------------------------------------------
* MAP OF CONCAT EXIT CALL
*------------------------------------------------------------------
PARMCONC DSECT
*
CONCFUNC DS    F                 FUNCTION CODE
CONCEXIT DS    A                 USER EXIT BAG ADDRESS
CONCBLKL DS    F                 NEXT DATA SET IN CONCAT BLOCK SIZE
CONCRECL DS    F                 NEXT DATA SET IN CONCAT RECORD LENGTH
CONCRECF DS    F                 NEXT DATA SET IN CONCAT RECORD FORMAT
CONCZLEN DS    A                 POINTER TO DATA LENGTH
PARMCONL EQU   *-PARMCONC
*
*------------------------------------------------------------------
* MAP OF LOG ROUTINE PARMLIST
*------------------------------------------------------------------
PARMLOG  DSECT
LOGSTR   DS    A                 ADDRESS OF THE NULL-TERMINATED STRING
PARMLOGL EQU   *-PARMLOG
*
*------------------------------------------------------------------
* EQUATES AND CONSTANTS
*------------------------------------------------------------------
EXITPARS EQU   4
EXITOPEN EQU   8
EXITREAD EQU   12
EXITCONC EQU   16
EXITWRIT EQU   20
EXITCLOS EQU   24
EXITP2HB EQU   28   NOT SUPPORTED YET
EXITHB2P EQU   32   NOT SUPPORTED YET
*
*  EXITMODE     VALUES
EXITINP  EQU   1
EXITOUT  EQU   2
EXITAPP  EQU   4
EXITUPD  EQU   8
*  RECFM        VALUES
EXITRECF EQU   X'80'
EXITRECV EQU   X'40'
EXITRECB EQU   X'10'
EXITRECS EQU   X'08'
EXITRECA EQU   X'04'
EXITRECU EQU   X'C0'
&SYSECT  CSECT
         MEND
         DS    OD
VXEXITL  EQU   *-VXEXIT
         SPACE 1
         MACRO
&LBL     VXENTER &DSA=,&WORKAREA=MEMABV,&VXEXIT=R10
         DROP
&LBL     CSECT
         USING &LBL,R11
         LR    R11,R15                 LOAD PROGRAM BASE
         USING VXEXIT,&VXEXIT
         L     &VXEXIT,4(,R1)          LOAD -> VXEXIT STRUCTURE
         AIF   ('&DSA' EQ 'NO').NODSA
         AIF   ('&DSA' EQ '').NODSA
         L     R15,&WORKAREA           LOAD -> DSA FROM VXEXIT
         ST    R15,8(,R13)             SET FORWARD CHAIN
         ST    R13,4(,R15)             SET BACKWARD CHAIN
         LR    R13,R15                 SET NEW DSA
         USING &DSA,R13
.NODSA  ANOP
         MEND
* - - - - - - - - - -
         MACRO
&LBL     VXRETURN &DSA=
         AIF   ('&LBL' EQ '').NOLBL
&LBL     DS    0H
.NOLBL   AIF   ('&DSA' EQ 'NO').NODSA
         L     R13,4(,R13)             LOAD PREVIOUS DSA
.NODSA  ANOP
         ST    R15,16(,R13)            SAVE RETURN CODE
         LM    R14,R12,12(R13)         RELOAD REGS
         BR    R14                     RETURN
         LTORG
         MEND
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SASCSRC  START 
*
* MAIN ENTRY POINT FOR ALL EXITS
*
         USING SASCSRC,R15
         STM   R14,R12,12(R13)
         L     R2,0(,R1)               LOAD FUNCTION CODE
         L     R15,CSRCFUNC(R2)        LOAD FUNCTION ADDRESS
         BR    R15
*
CSRCFUNC DS    0A                      CSRC FUNCTIONS
         DC    A(CSRCINIT)                  INITIALIZATION
         DC    A(CSRCPARS)                  PARSE CSRC OPTIONS
         DC    A(CSRCOPEN)                  OPEN EXIT
         DC    A(CSRCREAD)                  READ EXIT
         DC    A(CSRCCNCT)                  CONCATENATION BOUNDARY EXIT
         DC    A(CSRCWRIT)                  WRITE EXIT
         DC    A(CSRCCLOS)                  CLOSE EXIT
*
* INITIALIZATION EXIT
*
CSRCINIT VXENTER DSA=NO
         SPACE 1
         USING PARMINIT,R1
*
* THIS EXIT RUNS ONLY IN ESA AND ABOVE RELEASES
* WHICH SUPPORT DECOMPRESSION.
* THE CODE CHECKS FOR IT FIRST. IF NOT ESA, THE INIT FAILS
*
         L     R15,16                  LOAD CVT POINTER
         USING CVT,R15                 BASE FOR CVT MAPPING
         TM    CVTDCB,CVTOSEXT         EXTENSION PRESENT
         BNO   NOTESA                  FAIL, NOT ESA
         TM    CVTOSLV0,CVTXAX         SUPPORTS ESA
         BNO   NOTESA                  NOT AN ESA
         DROP  R15
         L     R3,=A(PWALENL)          SET WORK AREA LENGTH...
         L     R2,INITMALN
         ST    R3,0(,R2)               AS ABOVE THE 16M LINE LENGTH
         SLR   R15,R15                 GOOD RC
         XC    EFLAG1,EFLAG1           CLEAR
         OI    EFLAG1,EX_ALC           WILL USE ALLOC/FREE ROUTINES
         B     INITX                   RETURN
NOTESA   DS    0H
         LA    R15,BADOS
         ST    R15,ERRMSG              SAVE ERROR MESSAGE
INITX    DS    0H
         SPACE 1
         VXRETURN DSA=NO
BADOS    DC C'THIS SUPPORT IS NOT AVAILABLE IN THIS ENVIRONMENT'
         DC XL1'00'
*
*  PARSE EXIT
*
CSRCPARS VXENTER DSA=PWA
         USING PARMPARS,R4
         LR    R4,R1                   R4 IS PARMLIST BASE
         SPACE 1
         L     R6,PARSOPTL             R6 = OPTION NAME LENGTH
         LTR   R6,R6                   IF 0
         BZ    PARSR                      RETURN OK
         LA    R15,4                   SET BAD OPTION RC
         L     R7,PARSOPTN             R7 -> OPTION NAME
         L     R8,PARSVALL             R8 = OPTION VALUE LENGTH
         L     R9,PARSVAL              R9 -> OPTION VALUE (VAR NAME)
         SPACE 1
*---------------------------------------------*
* OPTION ACCEPTED IS:                         *
*    CSRC   RECL=                             *
*---------------------------------------------*
         C     R6,=F'4'                IF LENGTH NOT 4
*        BNE   PARSX                   RETURN WITH ERROR
         LTR   R8,R8                   IS IT =
         BNZ   PARSRECL                THEN CHECK FOR RECL=
         CLC   0(4,R7),=CL4'CSRC'      IF NOT 'CSRC'
         BNE   PARSX                   RETURN WITH ERROR
         B     PARSR                    ELSE RETURN OK
*---------------------------------------------*
* PARSE RECL=NUM                              *
*---------------------------------------------*
PARSRECL DS    0H
         CLC   0(4,R7),=CL4'RECL'      IF NOT 'RECL'
         BNE   PARSX                   RETURN WITH ERROR
         CH    R8,=H'16'               GREATER THAN 16
         BNL   PARSX                   INVALID VALUE
         BCTR  R8,0                    MINUS 1 FOR EXECUTE
         XC    TEMP,TEMP               CLEAR
         EX    R8,CONNUM               CONVERT TO NUMBER
*CONNUM  PACK  TEMP(0),0(R9)
         CVB   R0,TEMP                 CONVERT TO BINARY
         ST    R0,RECL                 SAVE RECL
         SPACE 1
PARSR    SLR   R15,R15                 RETURN OK
         SPACE 1
PARSX    VXRETURN DSA=PWA
CONNUM   PACK  TEMP(8),0(0,R9)           *** EXECUTE ****
*
* OPEN EXIT
*
CSRCOPEN VXENTER DSA=PWA
         USING PARMOPEN,R1
         SPACE 1
         LA    R15,NOINPUT             SET -> NO INPUT ERROR MESSAGE
         L     R4,RECL                 LOAD USER RECLEN
         LTR   R4,R4                   HAS IT BEEN SET?
         BNZ   *+8
         LH    R4,=Y(32676)            SET LRECL=32K BY DEFAULT
         SPACE 1
         LA    R15,DLENBIG             SET -> DATALENGTH TOO BIG MESSAGE
         L     R2,OPENZLEN
         L     R3,0(,R2)               R3 = DATA LENGTH OF EACH RECORD
         CR    R3,R4                   IF GREATER THAN CSRC MAXIMUM
         BH    OPENX                      RETURN ERROR
         SPACE 1
         ST    R4,0(,R2)               RETURN LENGTH TO THE SAS SYSTEM
         ST    R4,RECL                 SAVE LENGTH
*
* ALLOCATION OF BUFFER FOR INPUT RECORDS
*
         LA    R1,PARM                 POINT TO PARMAREA
         XC    PARM,PARM               CLEAR
         USING PARMALC,R1
         ST    R10,ALCEXIT             COPY HOST BAG POINTER
         LA    R15,MEMADDR
         ST    R15,ALCPTR              PLACE TO RETURN MEM ADDRESS
         ST    R4,ALCLEN               LENGTH OF MEMORY NEEDED
         L     R15,ALLOC               LOAD MEMORY ALLOCATE ROUTINE
         BALR  R14,R15                 ALLOCATION OF MEMORY
         LTR   R15,R15                 WAS MEMORY ALLOCATED?
         BNZ   OPENMEM                 IF NOT, OPERATION FAILS
*
* QUERY THE COMPRESS SERVICE
*
         LA    R0,1                    USE RUN LENGTH ENCODING
         CSRCESRV SERVICE=QUERY        QUERY IT
         LTR   R15,R15                 EVERYTHING OK
         BNZ   OPENERR                 IF NOT, FAIL WITH MESSAGE
         LTR   R1,R1                   REQUIRE WORK AREA
         BZ    OPENX                   IF NOT, END
         LR    R0,R1                   SAVE R1
         LA    R1,PARM                 POINT TO PARMLIST
         LA    R15,MEMWK               ALLOCATE WORK AREA
         ST    R15,ALCPTR              PLACE TO RETURN MEM ADDRESS
         ST    R0,ALCLEN               LENGTH OF MEMORY NEEDED
         L     R15,ALLOC               LOAD MEMORY ALLOCATE ROUTINE
         BALR  R14,R15                 ALLOCATION OF MEMORY
         LTR   R15,R15                 WAS MEMORY ALLOCATED?
         BNZ   OPENMEM                 IF NOT, OPERATION FAILS
         B     OPENX                   RETURN, OPERATION IS DONE
OPENERR  DS    0H
         XC    TEMP,TEMP               CONVERT RC TO DECIMAL
         CVD   R15,TEMP                CONVERT TO DECIMAL
         MVC   MSG(BADESRVL),BADESRV   MOVE IN SKELETON
         UNPK  MSG+BADESRVL-3(2),TEMP  UNPACK
         OI    MSG+BADESRVL-2,X'F0'    MAKE IT PRINTABLE
         LA    R15,MSG                 SET MESSAGE
         ST    R15,ERRMSG              SET -> ERROR MESSAGE, IF ANY
         LA    R15,8
         B     OPENX
OPENMEM  DS    0H
         LA    R15,NOMEMORY
         SPACE 1
OPENX    DS    0H
         ST    R15,ERRMSG              SET -> ERROR MESSAGE, IF ANY
*                                      R15 = EITHER 0 OR NONZERO
         VXRETURN DSA=PWA
*
NOINPUT  DC   C'CSRC: DECOMPRESS DOES NOT SUPPORT OUTPUT'
         DC   XL1'00'
NOFIXED  DC   C'CSRC: DECOMPRESS DOES NOT SUPPORT FIXED LENGTH RECORDS'
         DC   XL1'00'
DLENBIG  DC   C'DATASET DATALENGTH > CSRC MAXIMUM'
         DC   XL1'00'
NOMEMORY DC   C'CSRC: UNABLE TO OBTAIN MEMORY'
         DC    XL1'00'
BADESRV  DC    C'CSRC: NON ZERO RETURN CODE FROM QUERY, RC = '
BADESRVN DC    H'0'
         DC    XL1'00'
BADESRVL EQU   *-BADESRV
*---------------------------------------------------------------
* READ EXIT
*
* THIS EXIT DECOMPRESSES EACH RECORD
*---------------------------------------------------------------
CSRCREAD VXENTER DSA=PWA
         USING PARMREAD,R1
         SPACE 1
         L     R8,READRECL             R8 -> RECORD LENGTH
         L     R9,READRECA             R9 -> RECORD ADDRESS
         L     R3,0(,R8)               R3 = RECORD LENGTH
         L     R2,0(,R9)               R2 = RECORD ADDRESS
         L     R1,MEMWK                LOAD WORK AREA ADDRESS
         L     R4,MEMADDR              R4 = OUTPUT BUFFER
         L     R5,RECL                 R5 = OUTPUT BUFFER LENGTH
         CSRCESRV SERVICE=EXPAND
         LTR   R15,R15                 EVERYTHING OK
         BNZ   READERR                 IF NOT, SET ERROR AND RETURN
         L     R15,MEMADDR             START OF BUFFER
         SR    R4,R15                  MINUS LAST BYTE USED
         ST    R4,0(,R8)               LENGTH OF UNCOMPRESSED RECORD
         ST    R15,0(,R9)              SAVE UNCOMPRESSED REC ADDRESS
         SLR   R15,R15                 SET GOOD RC
         B     READX                   RETURN TO USER
READERR  DS    0H
         XC    TEMP,TEMP               CONVERT RC TO DECIMAL
         CVD   R15,TEMP                CONVERT TO DECIMAL
         MVC   MSG(EXPERRL),EXPERR     MOVE IN SKELETON
         UNPK  MSG+EXPERRL-3(2),TEMP   UNPACK
         OI    MSG+EXPERRL-2,X'F0'     MAKE IT PRINTABLE
         LA    R15,MSG                 SET MESSAGE
         ST    R15,ERRMSG              SET -> ERROR MESSAGE, IF ANY
         LA    R15,8
*
         SPACE 1
READX    DS    0H
         VXRETURN DSA=PWA
         SPACE ,
EXPERR   DC    C'CSRC NON ZERO RETURN CODE FROM EXPAND, RC = '
EXPERRN  DC    H'0'
         DC    XL1'00'
EXPERRL  EQU   *-EXPERR
*
*
* CONCATENATION EXIT
*
CSRCCNCT VXENTER DSA=PWA
         SPACE 1
         SLR   R15,R15
         VXRETURN DSA=PWA
*---------------------------------------------------------------
* WRITE EXIT
*
* THIS EXIT COMPRESSES EACH RECORD
*---------------------------------------------------------------
CSRCWRIT VXENTER DSA=PWA
         USING PARMWRIT,R1
         L     R8,WRITRECL             R8 -> RECORD LENGTH
         L     R9,WRITRECA             R9 -> RECORD ADDRESS
         L     R3,0(,R8)               R3 = RECORD LENGTH
         L     R2,0(,R9)               R2 = RECORD ADDRESS
         L     R1,MEMWK                LOAD WORK AREA ADDRESS
         L     R4,MEMADDR              R4 = OUTPUT BUFFER
         L     R5,RECL                 R5 = OUTPUT BUFFER LENGTH
         CSRCESRV SERVICE=COMPRESS
         LTR   R15,R15                 EVERYTHING OK
         BNZ   WRITERR                 IF NOT, SET ERROR AND RETURN
         L     R15,MEMADDR             START OF BUFFER
         SR    R4,R15                  MINUS LAST BYTE USED
         ST    R4,0(,R8)               LENGTH OF RECORD
         ST    R15,0(,R9)              SAVE NEW RECORD ADDRESS
         SLR   R15,R15                 SET GOOD RC
         B     WRITEX                  RETURN TO USER
WRITERR  DS    0H
         XC    TEMP,TEMP               CONVERT RC TO DECIMAL
         CVD   R15,TEMP                CONVERT TO DECIMAL
         MVC   MSG(WRTERRL),WRTERR     MOVE IN SKELETON
         UNPK  MSG+WRTERRL-3(2),TEMP   UNPACK
         OI    MSG+WRTERRL-2,X'F0'     MAKE IT PRINTABLE
         LA    R15,MSG                 SET MESSAGE
         ST    R15,ERRMSG              SET -> ERROR MESSAGE, IF ANY
         LA    R15,8
         SPACE 1
         SPACE 1
WRITEX   DS    0H
         VXRETURN DSA=PWA
WRTERR   DC    C'CSRC: NON ZERO RETURN CODE FROM COMPRESS, RC = '
WRTERRN  DC    H'0'
         DC    XL1'00'
WRTERRL  EQU *-WRTERR
         LTORG
*
* CLOSE EXIT
*
CSRCCLOS VXENTER DSA=PWA
         SLR   R15,R15
         LA    R1,PARM
         XC    PARM,PARM
         USING PARMFRE,R1
         ST    R10,FREEXIT
         L     R15,MEMADDR
         ST    R15,FREPTR
         L     R15,FREE
         BALR  R14,R15
         VXRETURN DSA=PWA
*
R0       EQU   0
R1       EQU   1
R2       EQU   2
R3       EQU   3
R4       EQU   4
R5       EQU   5
R6       EQU   6
R7       EQU   7
R8       EQU   8
R9       EQU   9
R10      EQU   10
R11      EQU   11
R12      EQU   12
R13      EQU   13
R14      EQU   14
R15      EQU   15
*
         VXEXIT ,
*
PWA      DSECT                     PROGRAM WORK AREA
PWASAVE  DS    32F                 SAVE AREA
TEMP     DS    D
RECL     DS    F
SAVE     DS    32F
PARM     DS    CL(PARMALCL)
MEMADDR  DS    F
MEMWK    DS    F
MSG      DS    CL200
PWALENL  EQU   *-PWA               LENGTH OF CSRC WORK AREA
         CVT DSECT=YES
*
         END