Programming Concepts Manual, Volume II

Operating System and Version:
VSI OpenVMS IA-64 Version 8.4-1H1 or higher
VSI OpenVMS Alpha Version 8.4-2L1 or higher
VSI OpenVMS x86-64 Version 9.2-1 or higher

Preface

1. About VSI

VMS Software, Inc. (VSI) is an independent software company licensed by Hewlett Packard Enterprise to develop and support the OpenVMS operating system.

2. Intended Audience

This manual is intended for system and application programmers. It presumes that its readers have some familiarity with the VSI OpenVMS programming environment, derived from the OpenVMS Programming Environment Manual and OpenVMS high-level language documentation.

3. Document Structure

The printed copy of the VSI OpenVMS Programming Concepts Manual is a two-volume manual. The second volume contains the following three parts:
  • Part I, OpenVMS Programming Interfaces: Calling a System Routine

  • Part II, I/O, System, and Programming Routines

  • Part III, Appendixes and Glossary

The chapters in Volume II provide information about the programming features of the OpenVMS operating system. A list of the chapters and a summary of their content follows:
  • Chapter 1 describes the format used to document system routine calls and explains where to find and how to interpret information about routine calls.

  • Chapter 2 describes the concepts and conventions used by common languages to invoke routines and pass data between them.

  • Chapter 3 describes a set of language-independent routines that establishes a common run-time environment for user programs.

  • Chapter 4 describes the system services available to application and system programs for use at run-time.

  • Chapter 5 describes the libraries that contain C header files for routines.

  • Chapter 6 describes the different I/O programming capabilities provided by the run-time library.

  • Chapter 7 describes how to use system services to perform input and output operations.

  • Chapter 8 describes the run-time library (RTL) routines that allow access to various operating system components.

  • Chapter 9 describes how cross-reference routines that are contained in a separate, shareable image are capable of creating across-reference analysis of symbols.

  • Chapter 10 describes the techniques available for sharing data and program code among programs.

  • Chapter 11 describes the system time format, and the manipulation of date/time and time conversion. It further describes how to obtain and set the current date and time, how to set and cancel timer requests, and how to schedule and cancel wakeups. The Coordinated Universal Time (UTC) system is also described.

  • Chapter 12 describes file attributes, strategies to access files, and file protection techniques.

  • Chapter 13 presents an overview of Extended File Specifications (for the OpenVMS Alpha and I64 platforms only).

  • Chapter 14 describes the DECdtm programming interfaces, and the DECdtm X/Open Distributed Transaction Processing XA interface.

  • Chapter 15 describes how to create user-written system services with privileged shareable images for VAX, Alpha, and I64 systems.

  • Chapter 16 describes the system services that establish protection by using identifiers, rights databases, and access control entries. This chapter also describes how to modify a rights list as well as check access protection.

  • Chapter 17 describes how to write an authentication and credential management (ACM) client program or update existing programs to be an ACM client program.

  • Chapter 18 describes how to create and use logical name services, how to use logical and equivalence names, and how to add and delete entries to a logical name table.

  • Chapter 19 describes how to use the LIB$INITIALIZE routine to initialize an image.

  • Appendix A describes the use of generic macros to specify argument lists with appropriate symbols and conventions in the system services interface to MACRO assembles.

  • Appendix B describes the data types that provide compatibility between procedure calls that support many different high-level languages.

  • Appendix C describes the DIGITAL Distributed Name Service (DECdns) Clerk by introducing the functions of the DECdns (SYS$DNS) system service and various run-time library routines.

  • Authentication Glossary contains definitions for terms used in Chapter 17.

4. Related Documents

For a detailed description of each run-time library and system service routine mentioned in this manual, see the OpenVMS Run-Time Library documentation and the VSI OpenVMS System Services Reference Manual.

You can find additional information about calling OpenVMS system services and Run-Time Library routines in your language processor documentation. You may also find the following documents useful:
  • VSI OpenVMS DCL Dictionary

  • VSI OpenVMS User's Manual

  • VSI OpenVMS Guide to OpenVMS File Applications

  • VSI OpenVMS Guide to System Security

  • VSI OpenVMS DECnet Networking Manual

  • OpenVMS Record Management Services documentation

  • VSI OpenVMS Utility Routines Manual

  • VSI OpenVMS I/O User's Reference Manual

5. VSI Encourages Your Comments

You may send comments or suggestions regarding this manual or any VSI document by sending electronic mail to the following Internet address: . Users who have VSI OpenVMS support contracts through VSI can contact for help with this product.

6. OpenVMS Documentation

The full VSI OpenVMS documentation set can be found on the VMS Software Documentation webpage at https://docs.vmssoftware.com.

7. Typographical Conventions

The following conventions are used in this manual:

ConventionMeaning
Ctrl/xA sequence such as Ctrl/x indicates that you must hold down the key labeled Ctrl while you press another key or a pointing device button.
PF1 xA sequence such as PF1 x indicates that you must first press and release the key labeled PF1 and then press and release another key (x) or a pointing device button.
EnterIn examples, a key name in bold indicates that you press that key.
A horizontal ellipsis in examples indicates one of the following possibilities:
  • Additional optional arguments in a statement have been omitted.

  • The preceding item or items can be repeated one or more times.

  • Additional parameters, values, or other information can be entered.

.
.
.
A vertical ellipsis indicates the omission of items from a code example or command format; the items are omitted because they are not important to the topic being discussed.
( )In command format descriptions, parentheses indicate that you must enclose choices in parentheses if you specify more than one.
[ ]In command format descriptions, brackets indicate optional choices. You can choose one or more items or no items. Do not type the brackets on the command line. However, you must include the brackets in the syntax for directory specifications and for a substring specification in an assignment statement.
|In command format descriptions, vertical bars separate choices within brackets or braces. Within brackets, the choices are optional; within braces, at least one choice is required. Do not type the vertical bars on the command line.
{ }In command format descriptions, braces indicate required choices; you must choose at least one of the items listed. Do not type the braces on the command line.
bold typeBold type represents the name of an argument, an attribute, or a reason. In command and script examples, bold indicates user input. Bold type also represents the introduction of a new term.
italic typeItalic type indicates important information, complete titles of manuals, or variables. Variables include information that varies in system output (Internal error number), in command lines (/PRODUCER=name), and in command parameters in text (where dd represents the predefined code for the device type).
UPPERCASE TYPEUppercase type indicates a command, the name of a routine, the name of a file, or the abbreviation for a system privilege.
Example

This typeface indicates code examples, command examples, and interactive screen displays. In text, this type also identifies website addresses, UNIX commands and pathnames, PC-based commands and folders, and certain elements of the C programming language.

A hyphen at the end of a command format description, command line, or code line indicates that the command or statement continues on the following line.
numbersAll numbers in text are assumed to be decimal unless otherwise noted. Nondecimal radixes—binary, octal, or hexadecimal—are explicitly indicated.

Part I. OpenVMS Programming Interfaces: Calling a System Routine

This part of this second volume describes the basic calling format for OpenVMS routines and system services. It also describes the STARLET structures and definitions for C programmers.

Chapter 1. Call Format to OpenVMS Routines

This chapter describes the format used to document system routine calls and explains where to find and how to interpret information about routine calls. Subsequent chapters provide more specific information about calling run-time library (RTL) routines and system services.

Note

The documentation format described in this chapter is generic; portions of it are used or not used, as appropriate, in the following OpenVMS manuals that document system routines:
  • VSI OpenVMS System Services Reference Manual: A-GETUAI
  • VSI OpenVMS System Services Reference Manual: GETUTC-Z
  • OpenVMS Run-Time Library manuals
  • VSI OpenVMS Utility Routines Manual
  • VSI OpenVMS Record Management Services Reference Manual

1.1. Overview

This chapter provides additional explanations for the following documentation categories for routines:
  • Format

  • Returns

  • Arguments

  • Condition values returned

However, some main categories in the routine format contain information requiring no explanation beyond that given in Table 1.1.
Table 1.1. Main Headings in the Documentation Format for System Routines
Main HeadingDescription

Routine Name

Always present. The routine entry point name appears at the top of the first page. It is usually followed by the English text name of the routine.

Routine Overview

Always present. Appears directly below the routine name and briefly explains what the routine does.

Format

Always present. Follows the routine overview and gives the routine entry point name and the routine argument list.

Returns

Always present. Follows the routine format and explains what information is returned by the routine.

Arguments

Always present. Follows the Returns heading and gives detailed information about each argument. If a routine takes no arguments, the word None appears.

Description

Optional. Follows the Arguments heading and contains information about specifications taken by the routine: interaction between routine arguments, if any; operation of the routine within the context of OpenVMS; user privileges needed to call the routine, if any; system resources used by the routine; and user quotas that might affect the operation of the routine.

Note that any restrictions on the use of the routine are always discussed first in the Description section. For example, any required user privileges or necessary system resources are explained first.

For some simple routines, a Description section is not necessary because the routine overview provides the needed information.

Condition Values Returned

Always present. Follows the Description section and lists the condition values (typically status or completion codes) that are returned by the routine.

Example

Optional. Follows the Condition Values Returned heading and contains one or more programming examples that illustrate how to use the routine, followed by an explanation.

All examples under this heading are complete. They have been tested and should run when compiled (or assembled) and linked. Throughout the manuals that document system routines, examples are provided in as many different programming languages as possible.

1.2. Format Heading

The following three types of information can be present in the format heading:
  • Procedure call format

  • Explanatory text

  • Jump to Subroutine (JSB) format (VAX only)

On VAX processors, all system routines have a procedure call format, but few system routines have JSB formats. If a routine has a JSB format, the format always appears after the routine's procedure call format.

1.2.1. Procedure Call Format

Procedure call formats can appear in many forms. The following four formats illustrate the meaning of syntactical elements, such as brackets and commas. General rules of syntax governing how to use procedure call formats are shown in Table 1.2.
Table 1.2. General Rules of Syntax for Procedure Call Formats

Element

Syntax Rule

Entry point names

Entry point names are always shown in uppercase characters.

Argument names

Argument names are always shown in lowercase characters.

Spaces

One or more spaces are used between the entry point name and the first argument, and between each argument.

Braces ({})

Braces surround two or more arguments. You must choose one of the arguments.

Brackets ([])

Brackets surround optional arguments. Note that commas can also be optional (see the comma element). Note that programming language syntax for optional arguments differs between languages. Refer to your language user's guide for more information.

Commas (,)

Between arguments, the comma always follows the space. If the argument is optional, the comma might appear either inside or outside the brackets, depending on the position of the argument in the list and on whether surrounding arguments are optional or required.

Null arguments

A null argument is a placeholding argument. It is used for one of the following reasons: (1) to hold a place in the argument list for an argument that has not yet been implemented by VSI but might be in the future; or (2) to mark the position of an argument that was used in earlier versions of the routine but is not used in the latest version (upward compatibility is thereby ensured because arguments that follow the null argument in the argument list keep their original positions). A null argument is always given the name nullarg.

In the argument list constructed when a procedure is called, both null arguments and omitted optional arguments are represented by argument list entries containing the value 0. The programming language syntax required to produce argument list entries containing 0 differs from language to language. See your language user's guide for language-specific syntax.

Format 1

This format illustrates the standard representation of optional arguments and best describes the use of commas as delimiters. Arguments enclosed within square brackets are optional. In most languages, if an optional argument other than a trailing optional argument is omitted, you must include a comma as a delimiter for the omitted argument.

ROUTINE_NAME arg1[, [arg2][, arg3]]

Typically, OpenVMS RMS system routines use this format when a maximum of three arguments appear in the argument list.

Format 2

When the argument list contains three or more optional arguments, the syntax does not provide enough information. If you omit the optional arguments arg3 and arg4 and specify the trailing argument arg5, you must use commas to delimit the positions of the omitted arguments.

ROUTINE_NAME arg1, [arg2], nullarg, [arg3],[arg4], arg5

Typically, system services, utility routines, and run-time library routines contain call formats with more than three arguments.

Format 3

In the following call format, the trailing four arguments are optional as a group; that is, you specify either arg2, arg3, arg4, and arg5, or none of them. Therefore, if you do not specify the optional arguments, you need not use commas to delimit unoccupied positions.

However, if you specify a required argument or a separate optional argument after arg5, you must use commas when arg2, arg3, arg4, and arg5 are omitted.

ROUTINE_NAME arg1[, arg2, arg3, arg4, arg5]

Format 4

In the following example, you can specify arg2 and omit arg3. However, whenever you specify arg3, you must specify arg2.

ROUTINE_NAME arg1[, arg2[, arg3]]

1.2.2. JSB Call Format (VAX only)

The JSB call format indicates that the named routine is called using the VAX JSB instruction. The routine returns using Return from Subroutine (RSB). You can use the JSB call format with only the VAX MACRO and VAX BLISS languages.

Explanatory Text

Explanatory text might follow the procedure call format or the JSB call format, or both. This text is present only when needed to clarify the format. For example, in the call format, you indicate that arguments are optional by enclosing them in brackets ([]). However, brackets alone cannot convey all the important information that might apply to optional arguments. For example, in some routines that have many optional arguments, if you select one optional argument, you must also select another optional argument. In such cases, text following the format clarifies this.

1.3. Returns Heading

The Returns heading contains a description of any information returned by the routine to the caller. A routine can return information to the caller in various ways. The following subsections discuss each possibility and then describe how this returned information is presented.

1.3.1. Condition Values Returned in a Register

Most routines return a condition value in register R0. This condition value contains various kinds of information, the most important for the caller (in bits <3:0>) being the completion status of the operation. You test the condition value to determine whether the routine completed successfully. On OpenVMS I64, the calling standard specifies that return status is returned in R8. As an aid to portable code, the MACRO complier automatically maps R0 to R8. See the VSI OpenVMS MACRO Compiler Porting and User's Guide for additional information.

On Alpha and I64 processors, a 32-bit condition value is represented in the Alpha register sign-extended to 64 bits.

If you program in high-level languages for OpenVMS environments, the fact that status information is returned by means of a condition value and that it is returned in a hardware register is of little importance because you receive this status information in the return (or status) variable. The run-time environment established for the high-level language program allows the status information in R0 (R8, R9 for I64) to be moved automatically to the user's return variable.

Nevertheless, for routines that return a condition value, the Returns heading in the documentation contains the following information:
OpenVMS usage: cond_value 
type:          longword (unsigned) 
access:        write only 
mechanism:     by value

The OpenVMS usage entry specifies the OpenVMS data type of the information returned. Because a condition value in any OpenVMS operating system environment is returned in a specific condition value structure, the OpenVMS usage entry is cond_value.

The type entry specifies the standard data type of the information returned. Because the condition value structure is 32 bits, the type heading is longword (unsigned).

The access entry specifies the way in which the called routine accesses the object. Because the called routine is returning the condition value, the routine writes the value into R0 (R8, R9 for I64), so the access heading is write only.

The mechanism heading specifies the passing mechanism used by the called routine in returning the condition value. Because the called routine is writing the condition value directly into R0 (R8, R9 for I64), the mechanism heading is by value. (If the called routine had written the address of the condition value into R0 (R8, R9 for I64), the passing mechanism would have been by reference).

Note that if a routine returns a condition value, another main heading in the documentation format (Condition Values Returned) describes the possible condition values that the routine can return.

1.3.2. Other Returned Values

If a routine returns actual data, the Returns heading in the documentation of that routine contains the following information (for example, from a math routine):
OpenVMS usage: floating_point 
type:          G_floating 
access:        write only 
mechanism:     by value

In this mathematics routine notation, the OpenVMS data type is floating_point and the standard data type is G_floating point. The meaning of the contents of the access and mechanism headings is discussed in Sections 1.4.3 and 1.4.4.

The registers used to return values vary with the type of the result and the specific hardware environment. For more information, see the VSI OpenVMS Calling Standard.

In addition, under the Returns heading, some text can be provided after the information about the type, access, and mechanism. This text explains other relevant information about what the routine is returning.

For example, because the routine is returning actual data in the VAX, Alpha, or I64 registers, the registers cannot be used to convey completion status information. All routines that return actual data in VAX, Alpha, or I64 registers must signal the condition value, which contains the completion status. Thus, the text under the Returns heading points out that the routine signals its completion status.

1.3.3. Condition Values Signaled

Although most routines return condition values, some routines choose to signal their condition values using the OpenVMS signaling mechanism. Routines can signal their completion status whether or not they are returning actual data in the hardware registers, but all routines that return actual data in the hardware registers must signal their completion status if they are to return this status information at all.

If a routine signals its completion status, text under the Returns heading explains this, and the Condition Values Signaled heading in the documentation format describes the possible condition values that the routine can signal.

VSI's system routines never signal condition values indicating success. Only error condition values are signaled.

1.4. Arguments Heading

Detailed information about each argument is listed in the call format under the Arguments heading. Arguments are described in the order in which they appear in the call format. If the routine has no arguments, the word None appears.

The following format is used to describe each argument:
argument-name 
OpenVMS usage:  OpenVMS data type 
type:           argument data type 
access:         argument access 
mechanism:      argument passing mechanism

A paragraph of structured text describing the arguments follows the argument format along with additional information, if needed.

1.4.1. OpenVMS Usage Entry

The purpose of the OpenVMS usage entry is to facilitate the coding of source-language data type declarations in application programs. Ordinarily, the standard data type, discussed in Section 1.4.2, is sufficient to describe the type of data passed by an argument. However, within the OpenVMS operating system environment, many system routines contain arguments whose conceptual nature or complexity requires additional explanation. For instance, when an argument passes the name of an event flag, the type entry longword (unsigned) alone does not indicate the nature of the value. In this instance, an accompanying OpenVMS usage entry, denoting the OpenVMS data type ef_number, further explains the actual usage.

See Table B.1 for a list of the possible OpenVMS usage entries and their definitions. Refer to the appropriate language implementation table in Appendix B to determine the correct syntax of the type declaration in the language you are using.

Note that the OpenVMS usage entry is not a traditional data type (such as the standard data types of byte, word, longword, and so on). It is significant only within the context of the OpenVMS operating system and is intended solely to expedite data declarations within application programs.

1.4.2. Type Entry

In actuality, an argument does not have a data type; rather, the data specified by an argument has a data type. The argument is merely the vehicle for passing data to the called routine. Nevertheless, the phrase argument data type is used to describe the standard data type of the data specified by the argument.

Procedure calls result in the construction of an argument list. (This process is described in the VSI OpenVMS Calling Standard). An argument list is a sequence of entries together with a count of the number of entries.

On VAX systems, an argument list is represented as a vector of longwords, where the first longword contains the count and each remaining longword contains one argument.

On Alpha systems, an argument list is represented as quadword entities that comprise an argument item sequence, partly in hardware registers and (when there are more than six arguments for Alpha) partly on the stack. The argument information (AI) register contains the argument count that specifies the number of 64-bit argument items.

For I64 systems, parameters are passed in a combination of general registers, floating-point registers, and memory, as described in Chapter 2 and as illustrated in Figure 2.12. The parameter list is formed by placing each individual parameter into fixed-size elements of the parameter list, referred to as parameter slots. Each parameter slot is 64 bits wide; parameters larger than 64 bits are placed in as many consecutive parameter slots as are needed to contain the entire parameter. The rules for allocation and alignment of parameter slots are described in Section 2.4.3.1. The contents of the first eight parameter slots are always passed in registers, while the remaining parameters are always passed on the memory stack, beginning at the caller's stack pointer plus 16 bytes.

When arguments are passed by descriptors, these standard data types are defined with symbolic codes. Table 1.3 lists the standard data types for VAX, Alpha, and I64 systems that can appear for the type entry in an argument description, along with their symbolic code (DTYPE) used in argument descriptors.

For a detailed description of each of the following symbolic codes, see the VSI OpenVMS Calling Standard.
Table 1.3. Standard Data Types and Their Descriptor Field Symbols

Data Type

Symbolic Code

Absolute date and time

DSC$K_DTYPE_ADT

Byte integer (signed)

DSC$K_DTYPE_B

Bound label value

DSC$K_DTYPE_BLV

Bound procedure value?

DSC$K_DTYPE_BPV

Byte (unsigned)

DSC$K_DTYPE_BU

COBOL intermediate temporary

DSC$K_DTYPE_CIT

D_floating

DSC$K_DTYPE_D

D_floating complex

DSC$K_DTYPE_DC

Descriptor

DSC$K_DTYPE_DSC

F_floating

DSC$K_DTYPE_F

F_floating complex

DSC$K_DTYPE_FC

G_floating

DSC$K_DTYPE_G

G_floating complex

DSC$K_DTYPE_GC

H_floating ?

DSC$K_DTYPE_H

H_floating complex ?

DSC$K_DTYPE_HC

S_floating (32-bit IEEE)?

DSC$K_DTYPE_FS

T_floating (64-bit IEEE)?

DSC$K_DTYPE_FT

X_floating (128-bit IEEE)?

DSC$K_DTYPE_FX

S_floating complex?

DSC$K_DTYPE_FSC

T_floating complex?

DSC$K_DTYPE_FTC

X_floating complex?

DSC$K_DTYPE_FXC

Longword integer (signed)

DSC$K_DTYPE_L

Longword (unsigned)

DSC$K_DTYPE_LU

Numeric string, left separate sign

DSC$K_DTYPE_NL

Numeric string, left overpunched sign

DSC$K_DTYPE_NLO

Numeric string, right separate sign

DSC$K_DTYPE_NR

Numeric string, right overpunched sign

DSC$K_DTYPE_NRO

Numeric string, unsigned

DSC$K_DTYPE_NU

Numeric string, zoned sign

DSC$K_DTYPE_NZ

Octaword integer (signed)

DSC$K_DTYPE_O

Octaword (unsigned)

DSC$K_DTYPE_OU

Packed decimal string

DSC$K_DTYPE_P

Quadword integer (signed)

DSC$K_DTYPE_Q

Quadword (unsigned)

DSC$K_DTYPE_QU

Character string

DSC$K_DTYPE_T

Aligned bit string

DSC$K_DTYPE_V

Varying character string

DSC$K_DTYPE_VT

Unaligned bit string

DSC$K_DTYPE_VU

Word integer (signed)

DSC$K_DTYPE_W

Word (unsigned)

DSC$K_DTYPE_WU

Unspecified

DSC$K_DTYPE_Z

Procedure entry mask?

DSC$K_DTYPE_ZEM

Sequence of instruction?

DSC$K_DTYPE_ZI

1.4.3. Access Entry

The access entry describes the way in which the called routine accesses the data specified by the argument, or access method. The following methods of access are most common:
  • Read only. Data upon which a routine operates, or data needed by the routine to perform its operation, must be read by the called routine. Such data is also called input data. When an argument specifies input data, the access entry is read only.

    The term only is present to indicate that the called routine does not both read and write (that is, modify) the input data. Thus, input data supplied by a variable is preserved when the called routine completes execution.

  • Write only. Data that the called routine returns to the calling program must be written into a location where the calling program can access it. Such data is also called output data. When an argument specifies output data, the access entry is write only.

    In this context, the term only is present to indicate that the called routine does not read the contents of the location either before or after it writes into the location.

  • Modify. When an argument specifies data that is both read and written by the called routine, the access entry is modify. In this case, the called routine reads the input data, which it uses in its operation, and then overwrites the input data with the results (the output data) of the operation. Thus, when the called routine completes execution, the input data specified by the argument is lost.

Following is a complete list of access methods that can appear under the access entry in an argument description:
  • Read only

  • Write only

  • Modify

  • Function call (before return)

  • JMP after unwind

  • Call after stack unwind

  • Call without stack unwind

For more information, see the VSI OpenVMS Calling Standard.

1.4.4. Mechanism Entry

The way in which an argument specifies the actual data to be used by the called routine is defined in terms of the argument passing mechanism. There are three basic passing mechanism types:
  • By value. When the argument in the argument list contains the actual data to be used by the routine, the actual data is said to be passed to the routine by value. In this case, the argument is the actual data.

  • By reference. When the argument in the argument list contains the address of the data to be used by the routine, the data is said to be passed by reference. In this case, the argument is a pointer to the data.

  • By descriptor. When the argument in the argument list contains the address of a descriptor, the data is said to be passed by descriptor. A descriptor consists of two or more longwords (depending on the type of descriptor used) that describe the location, length, and the OpenVMS standard data type of the data to be used by the called routine. In this case, the argument is a pointer to a descriptor that points to the actual data.

    There are several kinds of descriptors. Each one contains a value, or class, in the fourth byte of the first longword. The class identifies the type of descriptor it is. Each class has a symbolic code.

    Table 1.4 lists the types of descriptors and their corresponding code names. See the VSI OpenVMS Calling Standard for a detailed description of each descriptor class.


Table 1.4. Descriptor Classes of Passing Mechanisms

Passing Mechanism

Descriptor Symbolic Code

By descriptor, fixed-length (scalar)

DSC$K_CLASS_S

By descriptor, dynamic string

DSC$K_CLASS_D

By descriptor, array

DSC$K_CLASS_A

By descriptor, procedure

DSC$K_CLASS_P

By descriptor, decimal string

DSC$K_CLASS_SD

By descriptor, noncontiguous array

DSC$K_CLASS_NCA

By descriptor, varying string

DSC$K_CLASS_VS

By descriptor, varying string array

DSC$K_CLASS_VSA

By descriptor, unaligned bit string

DSC$K_CLASS_UBS

By descriptor, unaligned bit array

DSC$K_CLASS_UBA

By descriptor, string with bounds

DSC$K_CLASS_SB

By descriptor, unaligned bit string with bounds

DSC$K_CLASS_UBSB

1.4.5. Explanatory Text

For each argument, one or more paragraphs of explanatory text follow the OpenVMS usage, type, access, and mechanism entries. The first paragraph is highly structured and always contains information in the following sequence:
  1. A sentence or a sentence fragment that describes (1) the nature of the data specified by the argument, and (2) the way in which the routine uses this data. For example, if an argument were supplying a number, which the routine converts to another data type, the argument description would contain the following sentence fragment:

    Integer to be converted to an F_floating point number

  2. A sentence that expresses the relationship between the argument and the data that it specifies. This relationship is the passing mechanism used to pass the data and, for a given argument, is expressed in one of the following ways:
    1. If the passing mechanism is by value, the sentence should read as follows:

      The attrib argument is a longword that contains (or is) the bit mask specifying the attributes.

    2. If the passing mechanism is by reference, the sentence should read as follows:

      The objtyp argument is the address of a longword containing a value indicating whether the object is a file or a device.

    3. If the passing mechanism is by descriptor, the sentence should read as follows:

      The devnam argument is the address of a string descriptor of a logical name denoting a device name.

  3. Additional explanatory paragraphs that appear for each argument, as needed. For example, some arguments specify complex data consisting of many discrete fields, each of which has a particular purpose and use. In such cases, additional paragraphs provide detailed descriptions of each such field, symbolic names for the fields, if any, and guidance on their use.

1.5. Condition Values Returned Heading

A condition value is a longword that has the following uses on the OpenVMS VAX, OpenVMS Alpha, and OpenVMS I64 systems:
  • Indicates the success or failure of a called procedure

  • Describes an exception condition when an exception is signaled

  • Identifies system messages

  • Reports program success or failure to the command level

The VSI OpenVMS Calling Standard explains in detail the uses for the condition value and depicts its format and contents.

The Condition Values Returned heading describes the condition values that are returned by the routine when it completes execution without generating an exception condition. These condition values describe the completion status of the operation.

If a called routine generates an exception condition during execution, the exception condition is signaled; the exception condition is then handled by a condition handler (either user supplied or system supplied). Depending on the nature of the exception condition and on the condition handler, the called routine either continues normal execution or terminates abnormally.

If a called routine executes without generating an exception condition, the called routine returns a condition value in one or two of the following ways:
  • Condition Values Returned

  • Condition Values Returned in an I/O Status Block

  • Condition Values Returned in a Mailbox

  • Condition Values Signaled

The method used to return the condition value is indicated under the Condition Values Returned heading in the documentation of each routine. These methods are discussed individually in the following subsections.

Under these headings, a two-column list shows the symbolic code for each condition value the routine can return and an accompanying description. The description explains whether the condition value indicates success or failure and, if failure, what user action might have caused the failure and what to do to correct it. Condition values that indicate success are listed first.

Symbolic codes for condition values are defined by the system. Though the condition value consists of several fields, each of which can be interpreted individually for specific information, the entire condition value itself can be interpreted as an integer, and this integer has an equivalent symbolic code.

The three sections that follow discuss the ways in which the called routine returns condition values.

1.5.1. Condition Values Returned

The possible condition values that the called routine can return in general register R0 (R8, R9 for I64) are listed under the Condition Values Returned heading in the documentation. Most routines return a condition value in this way.

In the documentation of system services that complete asynchronously, both the Condition Values Returned and Condition Values Returned in the I/O Status Block headings are used. Under the Condition Values Returned heading, the condition values returned by the asynchronous service refer to the success or failure of the system service request—that is, to the status associated with the correctness of the syntax of the call, in contrast to the final status associated with the completion of the service operation. For asynchronous system services, condition values describing the success or failure of the actual service operation—that is, the final completion status—are listed under the Condition Values Returned in the I/O Status Block heading.

1.5.2. Condition Values Returned in an I/O Status Block

The possible condition values that the called routine can return in an I/O status block are listed under the Condition Values Returned in the I/O Status Block heading.

The routines that return condition values in the I/O status block are the system services that are completed asynchronously. Each of these asynchronous system services returns to the caller as soon as the service call is queued. This allows the continued use of the calling program during the execution of the service operations. System services that are completed asynchronously all have arguments that specify an I/O status block. When the system service operation is completed, a condition value specifying the completion status of the operation is written in the first word of this I/O status block.

Representing a condition value in a word-length field is possible for system services because the high-order segment of all system service condition values is 0. See cond_value in Table B.1 or Section 2.8 for the field detail of the condition value structure.

1.5.3. Condition Values Returned in a Mailbox

The possible condition values that the called routine can return in a mailbox are listed under the Condition Values Returned in a Mailbox heading.

Routines such as SYS$SNDOPR that return condition values in a mailbox send information to another process to perform a task. The receiving process performs the action and returns the status of the task to the mailbox of the sending process.

1.5.4. Condition Values Signaled

The possible condition values that the called routine can signal (instead of returning them in R0 (R8, R9 for I64) are listed under the Condition Values Signaled heading.

Routines that signal condition values as a way of indicating the completion status do so because these routines are returning actual data as the value of the routine.

As mentioned, the signaling of condition values occurs whenever a routine generates an exception condition, regardless of how the routine returns its completion status under normal circumstances.

Chapter 2. Basic Calling Standard Conventions

The VSI OpenVMS Calling Standard defines the concepts and conventions used by common languages to invoke routines and pass data between them. This chapter briefly describes the following calling standard conventions:
  • Register usage

  • Stack usage

  • Argument list

  • Argument passing

  • Returns

Refer to the VSI OpenVMS Calling Standard for more detail on calling conventions and for standards defining argument data types, descriptor formats, and procedures for condition handling and stack unwinding.

2.1. Hardware Registers

Registers in the hardware provide the necessary temporary storage for computation within OpenVMS software procedures. The number of registers available and their usage vary between the OpenVMS VAX, OpenVMS Alpha, OpenVMS I64, and OpenVMS x86-64 systems.

2.1.1. Register Usage for OpenVMS VAX

The calling standard defines several VAX registers and their use as listed in Table 2.1.
Table 2.1. VAX Register Usage

Register

Use

PC

Program counter

SP

Stack pointer

FP

Current stack frame pointer

AP

Argument pointer

R1

Environment value (when necessary)

R0, R1

Function return value registers

By definition, any called routine can use registers R2 through R11 for computation and the AP register as a temporary register.

2.1.2. Register Usage for OpenVMS Alpha

On Alpha systems, there are two groups of 64-bit wide, general-purpose Alpha hardware registers:
  • Integer

  • Floating-point

The first 32 general-purpose registers support integer processing; the second 32 support floating-point operations.

2.1.2.1. Integer Registers

The calling standard defines the Alpha general-purpose integer registers and their use as listed in Table 2.2.
Table 2.2. Alpha Integer Register Usage

Register

Usage

R0

Function value register. A standard call that returns a nonfloating-point function must return the function result in this register. The register can be modified by the called procedure without being saved and restored.

R1

Conventional scratch register. In a standard call, this register can be modified by the called procedure without being saved and restored.

R2–R15

Conventional saved registers. If a standard-conforming procedure modifies one of these registers, the procedure must save and restore it.

R16–R21

Argument registers. Up to six nonfloating-point items of the argument list are passed in these registers and the registers can be modified by the called procedure without being saved and restored.

R22–R24

Conventional scratch registers. The registers can be modified by the called procedure without being saved and restored.

R25

Argument information (AI) register. The register describes the argument list (see Section 2.4.2 for a detailed description) and can be modified by the called procedure without being saved and restored.

R26

Return address (RA) register. The return address must be passed in this register and can be modified by the called procedure without being saved and restored.

R27

Procedure value (PV) register. The procedure value of the procedure being called is passed in this register and can be modified by the called procedure without being saved and restored.

R28

Volatile scratch register. The contents of this register are always unpredictable after any external transfer of control either to or from a procedure.

R29

Frame pointer (FP). This register defines which procedure is the current procedure.

R30

Stack pointer (SP). This register contains a pointer to the top (start) of the current operating stack.

R31

ReadAsZero/Sink (RZ). Hardware defined: binary zero as a source operand, sink (no effect) as a result operand.

2.1.2.2. Floating-Point Registers

The calling standard defines the Alpha floating-point registers and their use as listed in Table 2.3.
Table 2.3. Alpha Floating-Point Register Usage

Register

Usage

F0

Floating-point function value register. In a standard call that returns a floating-point result in a register, this register is used to return the real part of the result. The register can be modified by the called procedure without being saved and restored.

F1

Floating-point function value register. In a standard call that returns a complex floating-point result in registers, this register is used to return the imaginary part of the result. This register can be modified by the called procedure without being saved and restored.

F2–F9

Conventional saved registers. If a standard-conforming procedure modifies one of these registers, the procedure must save and restore it.

F10–F15

Conventional scratch registers. The registers can be modified by the called procedure without being saved and restored.

F16–F21

Argument registers. Up to six floating-point arguments can be passed by value in these registers. These registers can be modified by the called procedure without being saved and restored.

F22–F30

Conventional scratch registers. The registers can be modified by the called procedure without being saved and restored.

F31

ReadAsZero/Sink. Hardware defined: binary zero as a source operand, sink (no effect) as a result operand.

2.1.3. Register Usage for OpenVMS I64

The Intel® Itanium® architecture defines 128 general registers, 128 floating-point registers, 64 predicate registers, 8 branch registers, and up to 128 application registers. The large number of architectural registers enable multiple computations to be performed without having to frequently spill and fill intermediate data to memory.

The instruction pointer is a 64-bit register that points to the currently executing instruction bundle.

This section describes the register conventions for OpenVMS I64.

OpenVMS I64 uses the following register types:
  • General

  • Floating-point

  • Predicate

  • Branch

  • Application

2.1.3.1. I64 Register Classes

Registers are partitioned into the following classes that define the way a register can be used within a procedure:
  • Scratch registers—may be modified by a procedure call; the caller must save these registers before a call if needed (caller save).

  • Preserved registers—must not be modified by a procedure call; the callee must save and restore these registers if used (callee save). A procedure using one of the preserved general registers must save and restore the caller's original contents, including the NaT bits associated with the registers, without generating a NaT consumption fault.

    One way to preserve a register is not to use it at all.

  • Automatic registers—saved and restored automatically by the hardware call/return mechanism.

  • Constant or Read-only registers—contain a fixed value that cannot be changed by the program.

  • Special registers—used in the calling standard call/return mechanism.

  • Global registers—shared across a set of cooperating routines as global static storage that happens to be allocated in a register. (Details regarding the dynamic lifetime of such storage are not addressed here).

OpenVMS I64 further defines the way that static registers can be used between routines:
  • Special registers—used in the calling standard call/return mechanism. (These are the same as the set of special registers in the preceding list of registers used within a procedure).

  • Input registers—may be used to pass information into a procedure (in addition to the normal stacked input registers).

  • Output registers—may be used to pass information back from a called procedure to its caller (in addition to the normal return value registers).

  • Volatile registers—may not be used to pass information between procedures, either as input or output.

2.1.3.2. I64 General Register Usage

There are 128, 64-bit general registers (R0—R127) that are used to hold values for integer and multimedia computations. Each of the 128 registers has one additional NaT (Not a Thing) bit that is used to indicate whether the value stored in the register is valid. Execution of I64 speculative instructions can result in a register's NaT bit being set. Register R0 is read only and contains a value of zero (0). Attempting to write to R0 will cause a fault.

The calling standard defines the usage of the OpenVMS general registers as listed in Table 2.4.
Table 2.4. I64 General Register Usage

Register

Class

Usage

R0

Constant

Always 0.

R1

Special

Global data pointer (GP). Designated to hold the address of the currently addressable global data segment. Its use is subject to the following conventions:
  1. On entry to a procedure, GP is guaranteed valid for that procedure.

  2. At any direct procedure call, GP must be valid (for the caller). This guarantees that an import stub can access the caller's linkage table.

  3. Any procedure call (indirect or direct) may modify GP unless the call is known to be local to the image.

  4. At procedure return, GP must be valid (for the returning procedure). This allows the compiler to optimize calls known to be local (an exception to convention 3).

The effect of these rules is that GP must be treated as a scratch register at a point of call (that is, it must be saved by the caller), and it must be preserved from entry to exit.

R2

Volatile

May not be used to pass information between procedures, either as inputs or outputs.

R3

Scratch

May be used within and between procedures in any mutually consistent combination of ways under explicit user control.

R4—R7

Preserved

General-purpose preserved registers. Used for any value that needs to be preserved across a procedure call.

May be used within and between procedures in any mutually consistent combination of ways under explicit user control.

R8—R9

Scratch

Return value. Can also be used as input (whether or not the procedure has a return value), but not in any additional ways.

R10—R11

Scratch

May be used within and between procedures in any mutually consistent combination of ways under explicit user control.

R12

Special

Memory stack pointer (SP). Holds the lowest address of the current stack frame. At a call, the stack pointer must point to a 0 mod 16 aligned area. The stack pointer is also used to access any memory arguments upon entry to a function. Except in the case of dynamic stack allocation, code can use the stack pointer to reference stack items without having to set up a frame pointer for this purpose.

R13

Special

Reserved as a thread pointer (TP).

R14—R18

Volatile

May not be used to pass information between procedures, either as inputs or outputs.

R19—R24

Scratch

May be used within and between procedures in any mutually consistent combination of ways under explicit user control.

R25

Special

Argument information (see Section 2.4.3.3).

R26—R31

Scratch

May be used within and between procedures in any mutually consistent combination of ways under explicit user control.

IN0—IN7

Automatic

Stacked input registers. Code may allocate a register stack frame of up to 96 registers with the ALLOC instruction, and partition this frame into three regions: input registers(IN0, IN1, ...), local registers (LOC0, LOC1, ...), and output registers (OUT0, OUT1, ...). R32–R39 (IN0–IN7) are used as incoming argument registers. Arguments beyond these registers appear in memory.

LOC0—LOC95

Automatic

Stacked local registers. Code may allocate a register stack frame of up to 96 registers with the ALLOC instruction, and partition this frame into three regions: input registers (IN0, IN1, ...), local registers (LOC0, LOC1, ...), and output registers (OUT0, OUT1, ...). LOC0-LOC95 are used for local storage.

OUT0—OUT7

Scratch

Stacked output registers. Code may allocate a register stack frame of up to eight registers with the ALLOC instruction, and partition this frame into three regions: input registers (IN0, IN1, ...), local registers (LOC0, LOC1, ...), and output registers (OUT0, OUT1, ...). OUT0-OUT7 are used to pass the first eight arguments in calls.

2.1.3.3. I64 Floating-Point Register Usage

There are 128, 82-bit floating-point registers (F0—F127) that are used for floating-point computations. The first two registers, F0 and F1, are read only and read as +0.0 and +1.0, respectively. Instructions that write to F0 or F1 will fault.

The calling standard defines the usage of the OpenVMS floating-point registers as listed in Table 2.5.
Table 2.5. I64 Floating-Point Register Usage

Register

Class

Usage

F0

Constant

Always 0.0.

F1

Constant

Always 1.0.

F2—F5

Preserved

Can be used for any value that needs to be preserved across a procedure call. A procedure using one of the preserved floating-point registers must save and restore the caller's original contents without generating a NaT consumption fault.

F6—F7

Scratch

May be used within and between procedures in any mutually consistent combination of ways under explicit user control.

F8—F9

Scratch

Argument/Return values. See Section 2.4.3 and Section 2.7.3 for the OpenVMS specifications for use of these registers.

F10—F15

Scratch

Argument values. See Section 2.4.3 for the OpenVMS specifications for use of these registers.

F16—F31

Preserved

Can be used for any value that needs to be preserved across a procedure call. A procedure using one of the preserved floating-point registers must save and restore the caller's original contents without generating a NaT consumption fault.

F32—F127

Scratch

Rotating registers or scratch registers.


Note

VAX floating-point data are never loaded or manipulated in the I64 floating-point registers. However, VAX floating-point values may be converted to IEEE floating-point values, which are then manipulated in the I64 floating-point registers.

2.1.3.4. I64 Predicate Register Usage

Predicate registers are single-bit-wide registers used for controlling the execution of predicated instructions. There are 64 one-bit predicate registers (P0—P63) that control conditional execution of instructions and conditional branches. The first register, P0, is read only and always reads true (1). The results of instructions that write to P0 are discarded.

The calling standard defines the usage of the OpenVMS predicate registers as listed in Table 2.6.
Table 2.6. I64 Predicate Register Usage

Register

Class

Usage

P0

Constant

Always 1.

P1—P5

Preserved

Can be used for any predicate value that needs to be preserved across a procedure call. A procedure using one of the preserved predicate registers must save and restore the caller's original contents.

P6—P13

Scratch

Can be used within a procedure as a scratch register.

P14—P15

Volatile

Cannot be used to pass information between procedures, either as input or output.

P16—P63

Preserved

Rotating registers.

2.1.3.5. I64 Branch Register Usage

Branch registers are used for making indirect branches. There are 8 64-bit branch registers (B0—B7) that are used to specify the target addresses of indirect branches.

The calling standard defines the usage of the OpenVMS branch registers as listed in Table 2.7.
Table 2.7. I64 Branch Register Usage

Register

Class

Usage

B0

Scratch

Contains the return address on entry to a procedure; otherwise a scratch register.

B1—B5

Preserved

Can be used for branch target addresses that need to be preserved across a procedure call.

B6—B7

Volatile

May not be used to pass information between procedures, either as input or output.

2.1.3.6. I64 Application Register Usage

Application registers are special-purpose registers designated for application use. The calling standard defines the usage of the OpenVMS application registers as listed in Table 2.8.
Table 2.8. I64 Application Register Usage

Register

Class

Usage

AR.FPSR

See Usage

Floating-point status register. This register is divided into the following fields:
  • Trap Disable Bits (bits 5–0)—Must be preserved by the callee, except for procedures whose documented purpose is to change these bits.

  • Status Field 0—Must be preserved by the callee, except for procedures whose documented purpose is to change these bits. The flag bits are the IEEE floating-point standard sticky bits and are part of the static state of the machine.

  • Status Field 1—Dedicated for use by divide and square root code, and must always be set to standard values at any procedure call boundary (including entry to exception handlers). These standard values are: trap disable set, round-to-nearest mode, 80-bit (extended) precision, widest range for exponent on, and flush-to-zero mode off. The flag bits are scratch.

  • Status Fields 2 and 3—At procedure calls and returns, the control bits in these status fields must agree with the control bits in status field 0 and the trap disable bits should always be set. The flag bits are always available for scratch use.

See VSI OpenVMS Calling Standard for further usage and initial value information.

AR.RNAT

Automatic

RSE NaT collection register. Holds the NaT bits for values stored by the register stack engine. These bits are saved automatically in the register stack backing store.

AR.UNAT

Preserved

User NaT collection register. Holds the NaT bits for values stored by the ST8.SPILL instruction. As a preserved register, it must be saved before a procedure can issue any ST8.SPILL instructions. The saved copy of AR.UNAT in a procedure's frame holds the NaT bits from the registers spilled by its caller; these NaT bits are thus associated with values local to the caller's caller.

AR.PFS

Special

Previous function state. Contains information that records the state of the caller's register stack frame and epilogue counter. It is overwritten on a procedure call; therefore, it must be saved before issuing any procedure calls, and restored prior to returning.

AR.BSP

Read-only

Backing store pointer. Contains the address in the backing store corresponding to the base of the current frame. This register may be modified only as a side effect of writing AR.BSPSTORE while the Register Stack Engine (RSE) is in enforced lazy mode.

AR.BSPSTORE

Special

Backing store pointer. Contains the address of the next RSE store operation. It may be read or written only while the RSE is in enforced lazy mode. Under normal operation, this register is managed by the RSE, and application code should not write to it, except when performing a stack switching operation.

AR.RSC

See Usage

RSE control; the register stack configuration register. This register is divided into the following fields:
  • Mode—Controls the RSE behavior, and has scratch behavior. On a return, this field may be set to a standard value.

  • Privilege level—Controls the privilege level at which the RSE operates, and may not be changed by non-privileged software.

  • Endian mode—Controls the byte ordering used by the RSE, and must never be changed by an application.

AR.LC

Preserved

Loop counter.

AR.EC

Automatic

Epilogue counter (preserved in AR.PFS).

AR.CCV

Scratch

Compare and exchange comparison value.

AR.ITC

Read-only

Interval time counter.

AR.K0—AR.K7

Read-only

Kernel registers.

AR.CSD

Scratch

Reserved for use as implicit operand registers in future extensions to the Itanium architecture. To ensure forward compatibility, OpenVMS considers these registers as part of the thread and process state.

AR.SSD

Scratch

Reserved for use as implicit operand registers in future extensions to the Itanium architecture. To ensure forward compatibility, OpenVMS considers these registers as part of the thread and process state.

2.1.4. Register Usage for OpenVMS x86-64

This section describes the register conventions for OpenVMS x86-64. OpenVMS uses the following register types:
  • General-purpose

  • Floating-point and related control/status

  • Segment

  • Legacy pseudo-registers

2.1.4.1. x86-64 Register Classes

The x86-64 registers are partitioned into the following classes that define the way a register can be used within a procedure:
  • Scratch registers—may be modified by a procedure call; the caller must save these registers before a call if needed (caller save).

  • Preserved registers—must not be modified by a procedure call; the callee must save and restore these registers if used (callee save). A procedure using one of the preserved general-purpose registers must save and restore the original content of the caller.

    One way to preserve a register is not to use it at all.

  • Special registers—used in the calling standard call/return mechanism.

  • Volatile registers—may be used as scratch registers within a procedure and are not preserved across a call; may not be used to pass information between procedures either as input or output.

2.1.4.2. x86-64 General-Purpose Register Usage

The calling standard defines the usage of the OpenVMS x86-64 general-purpose registers as listed in Table 2.9.
Table 2.9. x86-64 General-Purpose Register Usage
RegisterClassUsage
%rax %eax %ax %al %ahScratch
  • Pass the argument information.
  • 1st return value register.
%rbx %ebx %bx %bl %bhPreservedCallee-saved registers.
%rcx %ecx %cx %cl %chScratchPass the 4th argument to procedures.
%rdx %edx %dx %dl %dhScratch
  • Pass the 3rd argument to procedures.
  • 2nd return value register.
%rsi %esi %si %silScratchPass the 2nd argument to procedure.
%rdi %edi %di %dilScratchPass the 1st argument to procedures.
%rbp %ebp %bp %bplPreservedUsed as a frame pointer, if manifested in a register.
%rsp %esp %sp %splSpecialStack pointer.
%r8 %r8d %r8w %r8lScratchPass the 5th argument to procedures.
%r9 %r9d %r9w %r9lScratchPass the 6th argument to procedures.
%r10 %r10d %r10w %r10lScratchPass the environment value when calling a bound procedure.
%r11 %r11d %r11w %r11lVolatileAvailable for use in call stubs, trampolines, and other constructs.
  • %r12 %r12d %r12w %r12l
  • %r13 %r13d %r13w %r13l
  • %r14 %r14d %r14w %r14l
  • %r15 %r15d %r15w %r15l
PreservedCallee-saved registers.
RFLAGSPreservedThe Direction Flag (DF) bit must be zero at procedure call and return.
ScratchAll other bits.
%ripSpecialInstruction pointer, not directly addressable by software.

2.1.4.3. x86-64 Floating-Point Register Usage (SSE)

The base x86-64 architecture provides 16 SSE floating-point registers, each 128 bits wide.

Intel AVX (Advanced Vector Extensions) option provides 16 256-bit wide AVX registers (%ymm0%ymm15). The lower 128 bits of %ymm0%ymm15 are aliased to the respective 128-bit SSE registers (%xmm0%xmm15?).

Intel AVX-512 option provides 32 512-bit wide SIMD registers (%zmm0%zmm31). The lower 128 bits of %zmm0%zmm31 are aliased to the respective 128-bit SSE registers (%xmm0%xmm-31). The lower 256 bits of %zmm0%zmm31 are aliased to the respective 256-bit AVX registers (%ymm0%ymm31?).

In addition, Intel AVX-512 also provides 8 vector mask registers (%k0%k7), each 64 bits wide.

For the purposes of parameter passing and function return, %xmmN, %ymmN, and %zmmN refer to the same register. Only one of them can be used at a time.

Vector register is used to refer to either an SSE, AVX, or AVX-512 register (but not a vector mask register). This document often uses the name SSE to refer collectively to the SSE registers together with either the AVX or AVX-512 options.

The calling standard defines the usage of the OpenVMS x86-64 SSE floating-point registers as listed in Table 2.10.
Table 2.10. SSE (xmm, ymm and zmm) Register Usage
RegisterClassUsage
%xmm0 %ymm0 %zmm0Scratch
  • Pass the 1st argument to procedures.
  • 1st return value register.
%xmm1 %ymm1 %zmm1Scratch
  • Pass the 2nd argument to procedures.
  • 2nd return value register.
%xmm2 %ymm2 %zmm2ScratchPass the 3rd argument to procedures.
%xmm3 %ymm3 %zmm3ScratchPass the 4th argument to procedures.
%xmm4 %ymm4 %zmm4ScratchPass the 5th argument to procedures.
%xmm5 %ymm5 %zmm5ScratchPass the 6th argument to procedures.
%xmm6 %ymm6 %zmm6ScratchPass the 7th argument to procedures.
%xmm7 %ymm7 %zmm7ScratchPass the 8th argument to procedures.
  • %xmm8—%xmm31
  • %ymm8—%ymm31
  • %zmm8—%zmm31
ScratchTemporary registers.
MXCSR

Preserved

The control flags (bits 6-15) are preserved.
ScratchThe other bits are scratch.
The calling standard defines the usage of the OpenVMS x86-64 vector mask register as listed in Table 2.11.
Table 2.11. Vector Mask Register Usage
RegisterClassUsage
%k0—%k7ScratchTemporary registers

2.1.4.4. x86-64 Floating-Point Register Usage (FPU)

OpenVMS x86-64 applications may use the x87 registers though there is little reason to do so. Packed, single- and double-precision floating-point operations are usually performed in the SSE registers, while the 80-bit extended-precision floating-point format is not supported by the OpenVMS compilers or run-times.

The calling standard defines the usage of the OpenVMS x86-64 FPU floating-point registers as listed in Table 2.12.
Table 2.12. x87 Register Usage
RegisterClassUsage
%st0Scratch1st return value register.
%st1Scratch2nd return value register.
%st2—%st7ScratchTemporary registers.
%mm0—%mm7ScratchThe MMX registers. Overlay the x87 floating-point (%st0—%st7) registers.
Control WordPreservedStores the value of the control word.
Status WordScratchStores the value of the status word.
  • Tag Word
  • Operand Pointer
  • Instruction Pointer
Not used by applications.

The CPU should be in x87 mode, not MMX mode, on procedure entry and exit.

2.1.4.5. x86-64 Segment Register Usage

The calling standard defines the usage of the OpenVMS x86-64 segment registers as listed in Table 2.13.
Table 2.13. x86-64 Segment Register Usage
RegisterClassUsage
%cs %ds %ss %esManaged by OpenVMS and implicitly used by applications
%fsReserved to OpenVMS
%gsReserved to OpenVMS

2.1.4.6. x86-64 Bound Register Usage

Use of the x86-64 bound registers is deprecated on OpenVMS. The only support provided is to context switch the contents of the bound registers as part of the normal application context; they are otherwise unused and unsupported.

2.1.4.7. Legacy Pseudo-Registers

The OpenVMS MACRO compiler for x86-64 (XMACRO) generates code that uses a set of pseudo-registers to emulate the Alpha register set. The pseudo-register set consists of 32 64-bit registers (R0—R31). The contents of these pseudo-registers are well defined only at procedure calls and returns; otherwise, XMACRO uses pseudo-registers at its discretion. No special semantics are associated with the pseudo-registers, even for the registers that would otherwise be considered special or part of the Alpha hardware.

The pseudo-registers are invisible to high-level languages, except for BLISS and VSI C. BLISS linkage attributes and VSI C linkage pragmas may be used to access pseudo-registers on calls and returns. See Section 2.1.2, for more information regarding Alpha register conventions and usage.

Use of such registers for other than legacy applications from other OpenVMS environments is deprecated.

The pseudo-registers are stored as a per-thread vector of quadwords in memory.

alpha_reg_vector_t* LIB$GET_ALPHA_REG_VECTOR ();
Arguments:
None. 
Function Value Returned:
ptrPointer to the Alpha pseudo-register vector for the current thread.

LIB$GET_ALPHA_REG_VECTOR preserves all registers other than the return value register %rax.

Any procedure that accesses the pseudo-registers must make its own call to LIB$GET_ALPHA_REG_VECTOR to obtain the array address. Passing the array address to another procedure by any means is an error that may result in undefined behavior.

2.2. Stack Usage for Procedures

A stack is a last-in/first-out (LIFO) temporary storage area that the system allocates for every user process. The system keeps information about each routine call in the current image on the call stack. Then, each time you call a routine, the system creates a structure on the stack, defined as the stack frame.

Stack frames and call frames are synonymous. A call frame for each procedure has a specified format containing pointers and control information necessary in the transfer of control between procedures of a call chain. Stack frames (call frames) of standard calling procedures differ across OpenVMS VAX, Alpha, I64, and x86-64 systems.

2.2.1. Stack Procedure Usage for OpenVMS VAX

Figure 2.1 shows the format of the stack frame created for the called procedure by the CALLG or CALLS instruction. The stack frame (pointed to by SP) is in the context of the current procedure, and call frames (pointed to by FP) are the preserved stack frames of other active procedures in the call chain. The stack frame (call frame) for each procedure in the chain contains the following:
  • A pointer to the call frame of the previous procedure call, defined as the frame pointer (FP).

    Note that FP points at the condition handler longword at the beginning of the previous call frame. Unless the procedure has a condition handler, this longword contains all zeros. See the VSI OpenVMS Calling Standard for more information on condition handlers.

  • The argument pointer (AP) of the previous routine call.

  • The stored address (program count) of the point at which the routine was called. Specifically, this address is the program count from the program counter (PC) of the instruction following the call to the current routine.

  • The contents of other general registers. Based on a register save mask specified in the control information of the second longword, the system restores the saved contents of the identified registers to the calling routine when control returns to it.

Figure 2.1. Call Frame Generated by CALLG and CALLS Instructions
Call Frame Generated by CALLG and CALLS Instructions

The contents of the stack located at addresses following the call frame belong to the calling program; they should not be read or written by the called procedure, except as specified in the argument list. The contents of the stack located at addresses lower than the call frame (at FP) belong to interrupt and exception routines; they are modified continually and unpredictably.

The called procedure allocates local storage by subtracting the required number of bytes from the stack provided on entry. This local storage is freed automatically by the RET instruction.

2.2.1.1. Calling Sequence

At the option of the calling procedure, the called procedure is invoked using the CALLG or CALLS instruction, as follows:
     CALLG    arglst, procedure
     CALLS    argcnt, procedure
CALLS pushes the argument count argcnt onto the stack as along word and sets the argument pointer, AP, to the top of the stack. The complete sequence using CALLS follows:
     push     argn
     .
     .
     .
     push     arg1
     CALLS    #n, procedure

2.2.1.2. Call Frames on Return

If the called procedure returns control to the calling procedure, control must return to the instruction immediately following the CALLG or CALLS instruction. Skip returns and GOTO returns are allowed only during stack unwind operations.

The called procedure returns control to the calling procedure by executing the return instruction (RET).

Note that when a routine completes execution, the system uses the FP in the call frame of the current procedure to locate the frame of the previous procedure. The system then removes the stack frame of the current procedure from the stack.

2.2.2. Stack Procedure Usage for OpenVMS Alpha

On Alpha systems, when a standard procedure is called, the language compiler creates a stack frame for that procedure. The stack format of a stack frame procedure consists of a fixed part (the size of which is known at compile time) and an optional variable part. There are two basic types of stack frames:
  • Fixed-size

  • Variable-size

2.2.2.1. Fixed-Size Stack Frame

Figure 2.2 illustrates the format of the stack frame for a procedure with a fixed amount of stack. The SP register is the stack base pointer for a fixed-size stack. In this case, R29 (FP) typically contains the address of the procedure descriptor for the current procedure.

The optional parts of the stack frame are created only as required by the particular procedure. As shown in Figure 2.2, the field names within brackets are optional fields. The fixed temporary locations are optional sections of any stack frame that contain language-specific locations required by the procedure context of some high-level languages.

The register save area is a set of consecutive quadwords in which registers that are saved and restored by the current procedure are stored. The register save area (RSA) begins at the location pointed to by the RSA offset. The contents of the return address register (R26) are always saved in the first register field (SAVED_RETURN) of the register save area.

Use of the arguments passed in memory appending the end of the frame is described in Section 2.4.2. For more detail concerning the fixed-size stack frame, see the VSI OpenVMS Calling Standard.

Figure 2.2. Fixed-Size Stack Frame Format
Fixed-Size Stack Frame Format

2.2.2.2. Variable-Size Stack Frame

Figure 2.3 illustrates the format of the stack frame for procedures with a varying amount of stack when PDSC$V_BASE_REG_IS_FP is 1. In this case, R29 (FP) contains the address that points to the base of the stack frame on the stack. This frame-base quadword location contains the address of the current procedure's descriptor.

The optional parts of the stack frame are created as required by the particular procedure. As shown in Figure 2.3, field names within brackets are optional fields. The fixed temporary locations are optional sections of any stack frame that contain language-specific locations required by the procedure context of some high-level languages.

A compiler can use the stack temporary area pointed to by the SP base register for fixed local variables, such as constant-sized data items and program state, as well as for dynamically sized local variables. The stack temporary area may also be used for dynamically sized items with a limited lifetime, for example, a dynamically sized function result or string concatenation that cannot be directly stored in a target variable. When a procedure uses this area, the compiler must keep track of its base and reset SP to the base to reclaim storage used by temporaries.

The register save area is a set of consecutive quadwords in which registers saved and restored by the current procedure are stored. The register save area (RSA) begins at the location pointed to by the offset PDSC$W_RSA_OFFSET. The contents of the return address register (R26) is always saved in the first register field (SAVED_RETURN) of the register save area.

Use of the arguments passed in memory appending the end of the frame is described in Section 2.4.2. For more detail concerning the variable-size stack frame, see the VSI OpenVMS Calling Standard.

Figure 2.3. Variable-Size Stack Frame Format
Variable-Size Stack Frame Format

2.2.3. Stack Procedure Usage for OpenVMS I64

The I64 general registers are organized as a logically infinite set of stack frames that are allocated from a finite pool of physical registers.

Registers R0 through R31 are called global or static registers and are not part of the stacked registers. The stacked registers are numbered R32 up to a user-configurable maximum of R127. A called procedure specifies the size of its new stack frame using the alloc instruction. The procedure can use this instruction to allocate up to 96 registers per frame shared among input, output, and local values. When a call is made, the output registers of the calling procedure are overlapped with the input registers of the called procedure, thereby allowing parameters to be passed with no register copying or spilling. The hardware renames physical registers so that the stacked registers are always referenced in a procedure starting at R32.

Management of the register stack is handled by a hardware mechanism called the Register Stack Engine (RSE). The RSE moves the contents of physical registers between the general register file and memory without explicit program intervention. This provides a programming model that looks like an unlimited physical register stack to compilers; however, saving and restoring of registers by the RSE may be costly, so compilers should still attempt to minimize register usage.

2.2.3.1. Procedure Types

This calling standard defines the following basic types of procedures:
  • Memory stack procedure—allocates a memory stack and may maintain part or all of its caller's context on that stack.

  • Register stack procedure—allocates only a register stack and maintains its caller's context in registers.

  • Null frame procedure—allocates neither a memory stack nor a register stack and therefore preserves no context of its caller.

    Note

    Unlike an Alpha null frame procedure (see the VSI OpenVMS Calling Standard), an I64 null frame procedure does not execute in the context of its caller because the I64 call instruction (br.call) changes the register set so that only the caller's output registers are accessible in the called routine. The caller's input and local registers cannot be accessed at all. The call instruction also changes the previous frame state (PFS) of the I64 processor.

A compiler may choose which type of procedure to generate based on the requirements of the procedure in question. A calling procedure does not need to know what type of procedure it is calling.

Every memory stack procedure or register stack procedure must have an associated unwind description (see the VSI OpenVMS Calling Standard) that describes what type of procedure it is and other procedure characteristics. A null frame procedure may also have an associated unwind description. (If not, a default description applies). This data structure is used to interpret the call stack at any given point in a thread's execution. It is typically built at compile time and usually is not accessed at run time except to support exception processing or other rarely executed code.

Read access to unwind descriptions is provided through the procedural interfaces described in the VSI OpenVMS Calling Standard.

An unwind description for a procedure is provided for the following reasons:
  • To make invocations of that procedure visible to and interpretable by facilities such as the debugger, exception-handling system, and the unwinder.

  • To ensure that the context of the caller saved by the called procedure can be restored if an unwind occurs. (For a description of unwinding, see the VSI OpenVMS Calling Standard).

2.2.3.2. Memory Stack

The memory stack is used for local dynamic storage, spilled registers, and parameter passing. It is organized as a stack of procedure frames, beginning with the main program's frame at the base of the stack, and continuing towards the top of the stack with nested procedure calls. At the top of the stack is the frame for the currently active procedure. (There may be some system-dependent frames at the base of the stack, prior to the main program's frame, but an application program may not make any assumptions about them).

The memory stack begins at an address determined by the operating system, and grows towards lower addresses in memory. The stack pointer register (SP) always points to the lowest address in the current, topmost frame on the stack.

Each procedure creates its frame on entry by subtracting its frame size from the stack pointer, and removes its frame from the stack on exit by restoring the previous value of SP (usually by adding its frame size, but a procedure may save the original value of SP when its frame size varies).

Because the register stack is also used for the same purposes as the memory stack, not all procedures need a memory stack frame. However, every nonleaf procedure must save at least its return link and the previous frame marker, either on the register stack or on the memory stack. This ensures that there is an invocation context for every nonleaf procedure on one or both of the stacks.

2.2.3.3. Procedure Frames

A memory stack procedure frame consists of five regions, as illustrated in Figure 2.4.

Figure 2.4. Procedure Frame
Procedure Frame
These regions are:
  • Scratch area. This 16-byte region is provided as scratch storage for procedures that are called by the current procedure. Leaf procedures need not allocate this region. A procedure may use the 16 bytes pointed to by the stack pointer (SP) as scratch memory, but the contents of this area are not preserved by a procedure call.

  • Outgoing parameters. Parameters in excess of those passed in registers are stored in this region of the stack frame. A procedure accesses its incoming parameters in the outgoing parameter region of its caller's stack frame.

  • Frame marker (optional). This region may contain information required for unwinding through the stack (for example, a copy of the previous stack pointer).

  • Dynamic allocation. This variable-sized region (initially zero length) can be created as needed.

  • Local storage. A procedure can store local variables, temporaries, and spilled registers in this region. For conventions affecting the layout of this area for spilled registers, see the VSI OpenVMS Calling Standard.

Whenever control is transferred to another procedure, the stack pointer must be octaword aligned; at other times there is no stack alignment requirement. (A side effect of this is that the in-memory portion of the argument list will start on an octaword boundary). During a procedure invocation, the SP can never be set to a value higher than the SP at entry to that procedure invocation.

Note

A stack pointer that is not octaword aligned is valid only in a variable-sized frame because the unwind descriptor (MEM_STACK_F, see the VSI OpenVMS Calling Standard) for a fixed-size frame specifies the size in 16-byte units.

An application may not write to memory addresses lower than the stack pointer, because this memory area may be written to asynchronously (for example, as a result of exception processing).

Most procedures are expected to have a fixed-size frame, and the conventions are biased in favor of this. A procedure with a fixed-size frame may reference all regions of the frame with a compile-time constant offset relative to the stack pointer. Compilers should determine the total size required for each region, and pad the local storage area to make the total frame size a multiple of 16 bytes. The procedure can then create the frame by subtracting an immediate constant from the stack pointer in the prologue, and remove the frame by adding the same immediate constant to the stack pointer in the epilogue.

If a procedure has a variable-size frame (for example, a C routine that calls the alloca builtin), it should make a copy of SP to serve as a frame pointer before subtracting the initial frame size from the stack pointer. The procedure can then restore the previous value of the stack pointer in the epilogue without regard for how much dynamic storage has been allocated within the frame. It can also use the frame pointer to access the local storage region, because offsets from SP will vary.

A frame pointer is not required if both of the following conditions are true:
  • The procedure uses an equivalent method of addressing the local storage region correctly before and after dynamic allocation.

  • The code satisfies the conditions imposed by the stack unwind mechanism.

To expand a stack frame dynamically, the scratch area, outgoing parameters, and frame marker regions (which are always located relative to the current stack pointer), must be relocated to the new top of stack. If the scratch area and outgoing parameter area are both clear of any live values, there is no actual work involved in relocating these areas. For procedures with dynamically sized frames, it is recommended that the previous stack pointer value be stored in a local stacked general register instead of the frame marker, so that the frame marker is also empty. If the previous stack pointer is stored in the frame marker, the code must take care to ensure that the stack is always unwindable while the stack is being expanded (see the VSI OpenVMS Calling Standard).

Other issues depend on the compiler and the code being compiled. The standard calling sequence does not define a maximum stack frame size, nor does it restrict how a language system uses any stack frame region beyond those purposes described here. For example, the outgoing parameter region can be used as scratch storage whenever it is not needed for passing parameters.

2.2.3.4. Register Stack

General registers R32 through R127 form a register stack that is automatically managed across procedure calls and returns. Each procedure frame on the register stack is divided into two dynamically sized regions: one for input parameters and local variables, and one for output parameters.

On a procedure call, the registers are automatically renamed by the hardware so that the caller's output registers form the base of the register stack frame of the callee. On return, the registers are restored to the previous state, so that the input and local registers are preserved across the call.

The ALLOC instruction is used at the beginning of a procedure to allocate the input, local, and output regions; the sizes of these regions are supplied as immediate operands. A procedure is not required to issue an ALLOC instruction if it does not need to store any values in its register stack frame. It may write to the first N stacked registers, where N is the value of the argument count passed in the argument information (AI) register (see Section 2.4.3.3). It may not write to any other stack register without first issuing an ALLOC instruction.

Figure 2.5 illustrates the operation of the register stack across an example procedure call. In this example, the caller allocates eight input, twelve local, and four output registers; the callee allocates four input, six local, and five output registers with the following instruction:
ALLOC R36=rspfs, 4, 6, 5, 0

The actual registers to which the stacking registers are physically mapped are not directly addressable by the application software.

2.2.3.4.1. Input and Local Registers

The hardware makes no distinction between input and local registers. The caller's output registers automatically become the callee's register stack frame on a procedure call, with all registers initially allocated as output registers. An ALLOC instruction may increase or decrease the total size of the register stack frame, and may adjust the boundary between the input and local region and the output region.

The software conventions specify that up to eight general registers are used for parameter passing. Any registers in the input and local region beyond those eight may be allocated for use as preserved locals. Floating-point parameters may produce holes in the parameter list that is passed in the general registers; those unused input registers may also be used for preserved locals.

The caller's output registers do not need to be preserved for the caller. Once an input parameter is no longer needed, or has been copied elsewhere, that register may be reused for any other purpose within the procedure.

Figure 2.5. Operation of the Register Stack
Operation of the Register Stack
2.2.3.4.2. Output Registers

Up to eight output registers are used for passing parameters. If a procedure call requires fewer than eight general registers for its parameters, the calling procedure does not need to allocate more than are needed. If the called procedure expects more parameters, it will allocate extra input registers; these registers will be uninitialized.

A procedure may also allocate more than eight registers in the output region. While the extra registers may not be used for passing parameters, they can be used as extra scratch registers. On a procedure call, they will show up in the called procedure's output area as excess registers, and may be modified by that procedure. The called procedure may also allocate few enough total registers in its stack frame that the top of the called procedure's frame is lower than the caller's top-of-frame, but those registers will become available again when control returns to the caller.

2.2.3.4.3. Rotating Registers

A subset of the registers in the procedure frame may be designated as rotating registers. The rotating register region always starts with R32, and may be any multiple of eight registers in number, up to a maximum of 96 rotating registers. The renaming is under control of the Register Rename Base (RRB).

If the rotating registers include any or all of the output registers, software must be careful when using the output registers for passing parameters, because a non-zero RRB will change the virtual register numbers that are part of the output region. In general, software should ensure either that the rotating region does not overlap the output region, or that the RRB is cleared to zero before setting output parameter registers.

2.2.3.4.4. Frame Markers

The current application-visible state of the register stack is stored in an architecturally inaccessible register called the current frame marker. On a procedure call, this register is automatically saved by copying it to an application register, the previous function state (AR.PFS). The current frame marker is modified to describe a new stack frame whose input and local area is initially zero size, and whose output area is equal in size to the previous output area. On return, the previous frame state register is used to restore the current frame marker to its earlier value, and the base of the register stack is adjusted accordingly.

It is the responsibility of a procedure to save the previous function state register before issuing any procedure calls of its own, and to restore it before returning.

2.2.3.4.5. Backing Store for Register Stack

When the depth of the procedure call stack exceeds the capacity of the physical register file, the hardware frees physical registers by saving them into a memory stack. This backing store is distinct from the memory stack described in Section 2.2.3.2.

As returns unwind the procedure call stack, the hardware also restores previously-saved physical registers from the backing store.

The operation of this register stack engine (RSE) is mostly transparent to application software. While the RSE is running, application software may not examine the contents of the backing store, and may not make any assumptions about how much of the register stack is still in physical registers or in the backing store. In order to examine previous stack frames, application software must synchronize the RSE with the FLUSHRS instruction. Synchronizing the RSE forces all stack frames up to, but not including, the current frame to be saved in backing store, allowing the software to examine the contents of the backing store without asynchronous operations modifying the memory. Modifications to the backing store require setting the RSE to enforced lazy mode after synchronizing it, which prevents the RSE from doing any operations other than those required by calls and returns. The procedure for synchronizing the RSE and setting the mode is described in the Itanium® Software Conventions and Runtime Architecture Guide.

The backing store grows towards higher addresses. The top of the stack, which corresponds to the top of the previous procedure frame, is available in the Backing Store Pointer (BSP) application register. The BSP must always point to a valid backing store address, because the operating system may need to start the RSE to process an exception.

Backing store overflow is automatically detected by the OpenVMS operating system, which will either extend the backing store to allow continued operation or will raise an exception. Unlike for the memory stack (see Section 2.2.3.2), there are no specific rules or requirements that must be satisfied to facilitate detection of backing store overflow.

A NaT collection register is stored into the backing store following each group of 63 physical registers. The NaT bit of each register stored is shifted into the collection register. When the BSP reaches the quadword just before a 64-quadword boundary, the RSE stores the collection register. Software can determine the position of the NaT collection registers in the backing store by examining the memory address. This process is described in greater detail in the Itanium® Software Conventions and Runtime Architecture Guide.

2.2.4. Stack Procedure Usage for OpenVMS x86-64

The calling standard defines the following basic types of procedure:
  • Variable-size stack procedure (sometimes known as a normal procedure in industry x86-64 documentation)—allocates a memory stack that is addressable using either %rbp (the frame pointer register) or %rsp (the stack pointer register). The size of the stack may vary during the procedure execution. The called procedure may maintain a part or the whole context of its caller on that stack.

  • Fixed-size stack procedure (sometimes known as a framepointerless procedure in industry x86-64 documentation)—allocates a memory stack that is addressable only using %rsp (the stack pointer register). The size of the stack is fixed during the procedure execution. The called procedure may maintain a part or the whole context of its caller on that stack.

  • Null frame procedure (sometimes known as a frameless procedure in industry x86-64 documentation)—allocates no memory stack (other than the implicit saving of the caller return address that is a part of the CALL instruction). No context of its caller is saved.

All types of procedures allow use of 128 bytes of temporary storage below the address given in the stack pointer. This so-called red zone is not preserved across procedure calls, but is preserved by signal and condition handlers. Outside of the kernel, procedures may use this for temporary storage. Because hardware interrupts do not preserve the red zone, kernel code cannot use it. The use of the red zone can be disabled with a compiler option or pragma.

The red zone is useful in frameless leaf procedures (that call no other procedures). It gives them 128 bytes of scratch storage without the performance overhead of setting up and taking down a stack frame.

A compiler chooses which type of procedure to generate based on the requirements of the procedure in question. A calling procedure does not need to know what type of procedure it is calling.

Every variable-size stack or fixed-size stack procedure must have an associated unwind description (see the VSI OpenVMS Calling Standard) that provides information on the procedure type and its characteristics. A null frame procedure may also have an associated unwind description. (The default description applies if there is no unwind description). This data structure is used to interpret the call stack at any given point in a thread execution. It is built at compile time and usually is not accessed at run-time except to support exception processing or other rarely executed code.

2.2.4.1. Variable-Size Stack Procedures

Variable-size stack procedures allocate the stack that grows towards lower addresses. The stack pointer (SP) is contained in the %rsp register. The frame pointer (FP) is contained in the %rbp register. The stack pointer is normally 0mod16 aligned and must be 0mod16 aligned when making a call. Because the return address is pushed on the stack by the caller, the stack pointer is 8mod16 aligned on entry to a procedure. The %rbp register is saved immediately below the return address. The frame pointer points to the saved %rbp.

The resulting stack frame layout is illustrated in Figure 2.6.
Figure 2.6. Stack Frame for Variable-Size Stack Procedures
Stack Frame for Variable-Size Stack Procedures

2.2.4.2. Fixed-Size Stack Procedures

Fixed-size stack procedures allocate the stack that grows towards lower addresses. The stack pointer (SP) is contained in the %rsp register. No frame pointer (FP) is used, so that the %rbp register is available as an additional preserved register. The stack pointer is normally 0mod16 aligned and must be 0mod16 aligned when making a call. Because the return address is pushed on the stack by the caller, the stack pointer is 8mod16 aligned on entry to a procedure.

The resulting stack frame layout is illustrated in Figure 2.7.
Figure 2.7. Stack Frame for Fixed-Size Stack Procedures
Stack Frame for Fixed-Size Stack Procedures

2.2.4.3. Null Frame Procedures

A null frame procedure is almost a special case of a fixed-size stack procedure. It is like a fixed-size stack which has no local storage other than the return address that is pushed on the stack as a result of the call. Because no additional stack is allocated it is unlike a fixed-size stack in that the alignment of the stack pointer is 8mod16 (not 0mod16).

A null frame procedure is necessarily a leaf procedure because the stack pointer must be 0mod16 aligned in order to make a call.

The resulting stack frame layout is illustrated in Figure 2.8.
Figure 2.8. Stack Frame for Null Frame Procedures
Stack Frame for Null Frame Procedures

2.3. Procedure Representation

A procedure value is an address value that represents a procedure.

2.3.1. Procedure Values on OpenVMS VAX

On OpenVMS VAX systems, a procedure value is the address of the procedure entry mask that begins the actual code sequence of the procedure.

2.3.2. Procedure Values on OpenVMS Alpha

On OpenVMS Alpha systems, a procedure value in R27 is the address of the procedure descriptor that describes that procedure. So any OpenVMS Alpha procedure can be invoked by calling the stored address at offset 8 from the procedure descriptor (PDSC) starting address (procedure value).

2.3.3. Procedure Values on OpenVMS I64

On OpenVMS I64 systems, a procedure value is the address of a function descriptor, which consists of at least two quadword fields: the address of the entry point and the GP value required by that procedure.

Every procedure whose address is taken, or might be taken, must have a unique official function descriptor. The address of this function descriptor is used for the procedure value that is passed as a parameter or when two procedure values are compared. For other purposes, additional local function descriptors may be used for efficiency (notably in images other than the image that contains the procedure).

An official function descriptor for any procedure which might be callable from a VAX or Alpha translated image must include signature information. A local function descriptor used to call a procedure that might be part of a VAX or Alpha translated image must also include additional fields to facilitate the call. Both of these cases are described in the VSI OpenVMS Calling Standard.

A function descriptor for a bound procedure uses a special pseudo-GP value and includes an uplevel frame pointer. Such function descriptors are described in the VSI OpenVMS Calling Standard.

The several kinds of function descriptors are summarized in Table 2.14.
Table 2.14. Summary of Function Descriptor Kinds

Kinds and Roles

Size (Quadwords)

Local function descriptor without translated image support

2

Local function descriptor with translated image support (jacket function descriptor)

4

Official function descriptor without translated image support

3

Official function descriptor with translated image support

3

Bound function descriptor

6

Note that the different kinds of function descriptor are not self-identifying (that is, they do not contain any form of tag or kind field).

2.3.4. Procedure Values on OpenVMS x86-64

On OpenVMS x86-64 systems, a procedure value (a function pointer) is a pointer to code. To call through a procedure value, call through the value itself, not through a location in the memory pointed to by the value.

All procedure values must be representable in 32 bits. Because 32-bit addresses and pointers are always sign-extended before use, this means that the code they point to must reside in either the (hexadecimal) range 0..7FFFFFFF FFFFFFFF or 80000000 00000000..FFFFFFFF FFFFFFFF (see the VSI OpenVMS Programming Concepts Manual, Volume I for discussion of the structure of the OpenVMS address space). If the code is not in either of these regions, the linker creates a 32-bit-addressable trampoline for it. The trampoline code simply jumps to the procedure. The address of this trampoline becomes the value for that procedure.

Unbound procedures normally do not require an associated trampoline. They need a trampoline only if code in the same image takes the address of the procedure, or if it is a universal symbol.

Bound procedure values always point to trampolines. These trampolines are created by the containing procedure at the time it is called. When the bound procedure value trampolines pass control to the procedure, they pass an environment pointer (a pointer to the containing procedure stack frame) as an additional hidden parameter to the procedure.

2.4. Argument List

The calling standard defines a data structure called the argument list. An argument list is a sequence of locations in memory that represents a routine parameter list and possibly includes a function value. You use an argument list to pass information to a routine and receive results.

2.4.1. Argument Lists on OpenVMS VAX

On OpenVMS VAX systems, the first longword in an argument list (see Figure 2.9) stores the number of arguments (the argument count, n) as an unsigned integer value. The maximum argument count is 255. The remaining 24 bits of the first longword are reserved for OpenVMS and must be 0.

Both integer and floating-point values can be an argument passed in the argument list. Note that a 64-bit floating-point argument counts as 2 longword arguments in the list.

Figure 2.9. Structure of a VAX Argument List
Structure of a VAX Argument List

2.4.2. Argument Lists on OpenVMS Alpha

On OpenVMS Alpha systems, arguments are quadwords, and the calling program passes arguments in an argument item sequence. Each quadword in the sequence specifies a single argument. The argument item sequence is formed using R16—21 or F16—21 (a register for each argument). The argument item sequence can have a mix of integer and floating-point items that use both register types but must not repeat the same number. For example, an argument list might use R16, R17, F18, and R19. If there are more than six arguments, the argument items overflow to the end of the stack, as shown in Figure 2.10.

Figure 2.10. Alpha Argument List Format
Alpha Argument List Format

The calling procedure must pass to the called procedure information about the argument list. For high-level languages, this is performed by the language processor. In the argument information (AI) register (R25), the quadword format is the structure shown in Figure 2.11. The AI register contains the argument count in the first byte. Table 2.15 describes the argument information fields in detail.

Figure 2.11. Argument Information (AI) Register (R25) Format
Argument Information (AI) Register (R25) Format
Table 2.15. Contents of the Argument Information Register (R25)

Field Name

Contents

AI$B_ARG_COUNT

Unsigned byte <7:0> that specifies the number of 64-bit argument items in the argument list (known as the argument count).

AI$V_ARG_REG_
INFO

An 18-bit vector field <25:8> divided into six groups of 3 bits that correspond to the six arguments passed in registers. These groups describe how each of the first six arguments are passed in registers with the first group <10:8> describing the first argument. The encoding for each group for the argument register usage follows:

Value

Name

Meaning

0

AI$K_AR_I64

64-bit or 32-bit sign-extended to 64-bit argument passed in an integer register (including addresses).

or

Argument is not present.

1

AI$K_AR_FF

F_floating argument passed in a floating register.

2

AI$K_AR_FD

D_floating argument passed in a floating register.

3

AI$K_AR_FG

G_floating argument passed in a floating register.

4

AI$K_AR_FS

S_floating argument passed in a floating register.

5

AI$K_AR_FT

T_floating argument passed in a floating register.

6, 7

Reserved.

Bits 26—63

Reserved and must be 0.

2.4.3. Argument Lists on OpenVMS I64

On OpenVMS I64 systems, parameters are passed in a combination of general registers, floating-point registers, and memory, as illustrated in Figure 2.12.

The parameter list is formed by placing each individual parameter into fixed-size elements of the parameter list, referred to as parameter slots. Each parameter slot is 64 bits wide; parameters larger than 64 bits are placed in as many consecutive parameter slots as are needed to contain the entire parameter. The rules for allocation and alignment of parameter slots are described in Section 2.4.3.1.

The contents of the first eight parameter slots are always passed in registers, while the remaining parameters are always passed on the memory stack, beginning at the caller's stack pointer plus 16 bytes. The caller uses up to eight of the registers in the output region of its register stack for integer and VAX floating-point parameters, and up to eight floating-point registers for IEEE floating-point parameters. The maximum number of registers used is eight.

Figure 2.12. Parameter Passing in Registers and Memory
Parameter Passing in Registers and Memory

To accommodate variable argument lists in the C language, there is a fixed correspondence between parameter slots; the first parameter slot is always in either the first general output register or the first floating-point register (never both), the second parameter slot is always in the second general output register or the second floating-point register (never both), and so on. This allows a procedure to spill its register parameters easily to memory to form the argument home area before stepping through the parameter list with a pointer. The Argument Information register (AI) makes this possible, as explained in Section 2.4.3.3.

A procedure can assume that the NaT bits on its incoming general register arguments are clear, and that the incoming floating-point register arguments are not NaTVals. A procedure making a call must ensure only that registers containing actual parameters are clear of NaT bits or NaTVals; registers not used for actual parameters are undefined.

The parameter passing mechanisms for I64 are generally the same as for Alpha and are included here for completeness. Two notable difference between Alpha and I64 are that the first six parameter slots are passed in registers for Alpha, while for I64 the first eight parameter slots are passed in registers; and that I64 passes VAX floating-point parameters in general registers.

2.4.3.1. Allocation of Parameter Slots

Parameter slots are allocated for each parameter, based on the parameter passing mechanism, type, and size, treating each parameter in sequence, from left to right. The rules for allocating parameter slots and placing the contents within the slot are given in Table 2.16. The allocation column of the table indicates how parameter slots are allocated to each type of parameter.
Table 2.16. Rules for Allocating Parameter Slots

Type

Size (Bits)

Number of Slots

Integer, small set

1-64

1

Address/pointer (including all types passed by reference or descriptor)

64

1

IEEE single-precision floating-point (S_floating)

32

1

IEEE single-precision floating-point complex (S_floating)

64

2

IEEE double-precision floating-point (T_floating)

64

1

IEEE double-precision floating-point complex (T_floating)

128

2

IEEE quad-precision floating-point (X_floating)

64 (by reference)

1

IEEE quad-precision floating-point complex (X_floating)

64 (by reference)

1

Aggregates (noncomplex)

any

(size+63)/64

VAX single-precision floating-point (F_floating)

32

1

VAX single-precision floating-point complex (F_floating)

64

2

VAX double-precision floating-point (D_ & G_floating)

64

1

VAX double-precision floating-point complex (D_ & G_floating)

128

2

Note

These rules are applied based on the type of the parameter after any type-promotion rules specified by the language have been applied. For example, a short integer passed without a function prototype in C is promoted to the int type, and is then passed according to the rules for the int type.

OpenVMS does not support passing the I64 double-precision extended floating-point type (__float80), although that type may be used from time to time in code generation sequences.

This placement policy does not ensure that parameters greater than 64 bits in size will fall on a natural alignment boundary if passed in memory. Such parameters may need to be copied by the called procedure into an aligned temporary prior to use, or accessed in a way that does not depend on natural alignment.

2.4.3.2. Normal Register Parameters

The first eight parameter slots (64 bytes) are passed in registers, according to the following rules:
  • These eight argument slots are associated, one-to-one, with the stacked output general registers, as shown in Figure 2.12.

  • Integral scalar parameters, (including addresses and pointers), VAX floating-point parameters, and aggregate parameters in these slots are passed only in the corresponding output general registers.

  • Aggregate parameters in these slots are passed by value only in the corresponding output general registers. The aggregate is treated as a sequence of 64-bit integral values, with each value allocated into the next available slot in aggregate memory address order. If the size of the aggregate is not an even multiple of 64 bits, then the unused bits in the last slot are undefined.

  • If an aggregate or VAX floating-point complex parameter straddles the boundary between slot 7 and slot 8, the part that lies within the first eight slots is passed in general registers, and the remainder is passed in memory, as described in Table 2.17.

    Complex values (other than IEEE quad-precision floating-point complex), in those languages that include complex types, are passed as a pair of floating-point values (either single-precision or double-precision as appropriate). It is possible for the first of the two floating-point values in a complex value to occupy the last output register slot; in this case, the second floating-point value is passed in memory. IEEE quad-precision floating-point complex values are passed by reference.

  • IEEE single-precision and double-precision floating-point scalar parameters are passed in the corresponding floating-point register slot. IEEE quad-precision floating-point scalar parameters are passed by reference in the corresponding output general registers.

When IEEE floating-point parameters are passed in floating-point registers, they are passed in the register format, rounded to the appropriate precision. They are never passed in the general registers unless part of an aggregate, in which case they are passed in the aggregate memory format. When VAX floating-point parameters are passed in general registers, they are passed in memory format.

Parameters allocated beyond the eighth parameter slot are never passed in registers.

Unsigned integral (except unsigned 32-bit), set, and VAX floating-point values passed in registers are zero-filled; signed integral values as well as unsigned 32-bit integral values are sign-extended to 64 bits. For all other types passed in the general registers, unused bits are undefined.

Note

Bit 31 is replicated in bits 32—63, even for unsigned 32-bit integers.

The rules contained in this section are summarized in Tables 2.17 and 2.18.
Table 2.17. Unused Bits in Passed Data

Data Type
(OpenVMS Names)

Type Designator?

Data Size (bytes)

Register Extension Type

Memory Extension Type

Byte logical

DSC$K_DTYPE_BU

1

Zero64

Zero64

Word logical

DSC$K_DTYPE_WU

2

Zero64

Zero64

Longword logical

DSC$K_DTYPE_LU

4

Sign64

Sign64

Quadword logical

DSC$K_DTYPE_QU

8

Data64

Data64

Byte integer

DSC$K_DTYPE_B

1

Sign64

Sign64

Word integer

DSC$K_DTYPE_W

2

Sign64

Sign64

Longword integer

DSC$K_DTYPE_L

4

Sign64

Sign64

Quadword integer

DSC$K_DTYPE_Q

8

Data64

Data64

F_floating

DSC$K_DTYPE_F

4

VAXF64

Data32

D_floating

DSC$K_DTYPE_D

8

VAXDG64

Data64

G_floating

DSC$K_DTYPE_G

8

VAXDG64

Data64

F_floating complex

DSC$K_DTYPE_FC

2 * 4

2*VAXF64

2*Data32

D_floating complex

DSC$K_DTYPE_DC

2 * 8

2*VAXDG64

2*Data64

G_floating complex

DSC$K_DTYPE_GC

2 * 8

2*VAXDG64

2*Data64

S_floating

DSC$K_DTYPE_FS

4

Hard

Data32

T_floating

DSC$K_DTYPE_FT

8

Hard

Data64

X_floating

DSC$K_DTYPE_FX

16

N/A

N/A

S_floating complex

DSC$K_DTYPE_FSC

2 * 4

2*Hard

2*Data32

T_floating complex

DSC$K_DTYPE_FTC

2 * 8

2*Hard

2*Data64

X_floating complex

DSC$K_DTYPE_FXC

2 * 16

N/A

N/A

Small structures of 8 bytes or less

N/A

≤8

Nostd

Nostd

Small arrays of 8 bytes or less

N/A

≤8

Nostd

Nostd

32-bit address

N/A

4

Sign64

Sign64

64-bit address

N/A

8

Data64

Data64

Table 2.18 contains the defined meanings for the extension type symbols used in Table 2.17.
Table 2.18. Extension Type Codes

Sign Extension Type

Defined Function

Sign64

Sign-extended to 64 bits.

Zero64

Zero-extended to 64 bits.

Data32

Data is 32 bits. The state of bits <63:32> is unpredictable.

2*Data32

Two single-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data32).

Data64

Data is 64 bits.

2*Data64

Two double-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data64).

VAXF64

Data is 64 bits. Low-order 32 bits are the same as the F_floating memory format and the high-order 32 bits are zero. (Used only in a general register, never in a floating-point register).

VAXDG64

Data is 64 bits. Uses the corresponding D_floating or G_floating memory format. (Used only in a general register, never in a floating-point register.)

2*VAXF64

Two single-precision parts of the complex value are stored in memory as independent floating-point values (each handled as VAXF64).

2*VAXDG64

Two double-precision parts of the complex value are stored in memory as independent floating-point values (each handled as VAXDG64).

Hard

Passed in the layout defined by the hardware SRM.

2*Hard

Two floating-point parts of the complex value are stored in a pair of registers as independent floating-point values (each handled as Hard).

Nostd

State of all high-order bits not occupied by the data is unpredictable across a call or return.

2.4.3.3. Argument Information (AI) Register

In addition to the normal parameters, an implicit argument information value is passed in register R25, the Argument Information (AI) register. This value is shown in Figure 2.13. Note that I64 passes eight arguments in registers, while Alpha passes six arguments in registers.

Figure 2.13. Argument Information Register Representation
Argument Information Register Representation

Argument Count is an unsigned byte that specifies the number of 64-bit argument slots used for the argument list. (Note that single- and double-precision complex values use two slots, which is reflected in this count).

Argument Register Information is a contiguous group of eight 3-bit fields that correspond to the eight arguments passed in registers. The first group, bits <10:8>, describes the first argument; the second group, bits <13:11>, describes the second argument; and so on. The encoding for each group is described in Table 2.19.
Table 2.19. Argument Information Register Codes

Value

OpenVMS Name

Meaning

0

AI$K_AR_I64

64-bit or 32-bit sign-extended to 64-bit argument passed in an integer register (including addresses).

or

Argument is not present.

1

AI$K_AR_FF

F_floating (also known as VAX single-precision floating-point) argument passed in a general register.

2

AI$K_AR_FD

D_floating (also known as VAX double-precision floating-point) argument passed in a general register.

3

AI$K_AR_FG

G_floating (also known as VAX double-precision floating-point) argument passed in a general register.

4

AI$K_AR_FS

S_floating (also known as IEEE single-precision floating-point) argument passed in a floating-point register.

5

AI$K_AR_FT

T_floating (also known as IEEE double-precision floating-point) argument passed in a floating-point register.

6,7

Reserved.

2.4.3.4. Memory Stack Parameters

The remainder of the parameter list, beginning with slot 8, is passed in the outgoing parameter area of the memory stack frame, as described in the VSI OpenVMS Calling Standard. Parameters are mapped directly to memory, with slot 8 placed at location SP+16, slot 9 placed at location SP+24, and so on. Each argument is stored in memory as a series of one or more 64-bit storage units, with unused bits in the last unit undefined.

2.4.3.5. Variable Argument Lists

The rules above support variable-argument list functions in both the K&R and the ANSI dialects of the C language. (Note that argument location is independent of whether a prototype is in scope).

The nth argument is in either Rn or Fn regardless of the type of parameter in the preceding register slot. Therefore, a function with variable arguments may assume that the variable arguments that lie within the first eight argument slots can be found in either the stacked input integer registers (IN0-IN7), or in the floating-point parameter registers (F8-F15). Using the information codes from the AI (Argument Information) register (see Table 2.19), the function can then store these registers to memory using the 16-byte scratch area for IN6/F14 and IN7/F15, and up to 48 bytes at the base of its own stack frame for IN0/F8-IN5/F13, as necessary. This arrangement places all of the variable parameters in one contiguous block of memory.

2.4.3.6. Pointers to Formal Parameters

Whenever the address is formed of a formal parameter that is passed in a register, the compiler must store the parameter to the stack, as it would for a variable argument list.

2.4.3.6.1. Languages Other than C

The placement of arguments in general registers versus floating-point registers does not depend on any notion or concept of a prototype being in scope. It is therefore applicable to all languages at all times.

2.4.3.7. Rounding Floating-Point Values

There must be no difference in behavior between a floating-point parameter passed directly in a register and a floating-point parameter that has been stored to memory and reloaded. In either case, the floating-point value must be the same. This implies that floating-point parameters passed in floating-point registers must be explicitly rounded to the proper precision by the caller.

2.4.4. Argument Lists on OpenVMS x86-64

On OpenVMS x86-64 systems, procedure parameters are passed in registers and/or on the stack.

2.4.4.1. Scalar Argument Types

The following memory locations are used for passing scalar argument types to procedures:
  • the six general-purpose registers (%rdi, %rsi, %rdx, %rcx, %r8, and %r9)

  • the eight XMM registers (%xmm0—%xmm7)

  • the stack.


Table 2.20. Memory Locations Used for Passing Scalar Argument Types and Return Values

Nominal Type
[OpenVMS Type Code]
(prefix DSC$K_DTYPE_)

Argument LocationReturn Value Location
Pointer [Q]

The next available general-purpose register. Otherwise, in the next argument slot on the stack.

General-purpose register %rax
Boolean [B, BU]
Integers (size ≤ 64 bits) [B, W, L, Q, BU, WU, LU, QU]
Integers (64 < size ≤ 128 bits) [O, OU]

The next two available general-purpose registers. Otherwise, in the next two argument slots on the stack.

General-purpose registers %rax (low half) and %rdx (high half)
VAX float (F_floating, D_floating, and G_floating) [F, D, G]

The next available general-purpose register. Otherwise, in the next argument slot on the stack.

General-purpose register %rax
IEEE single-precision float (S_floating) [FS]

Bits 31:0 of the next available XMM register. Otherwise, in the next argument slot on the stack.

Bits 31:0 of register %xmm0
IEEE double-precision float (T_floating) [FT]

Bits 63:0 of the next available XMM register. Otherwise, in the next argument slot on the stack.

Bits 63:0 of register %xmm0
IEEE quadruple-precision float (X_floating) [FX]

The next available XMM register. Otherwise, in the next two argument slots on the stack.

Register %xmm0
VAX complex single-precision float (F_floating) [FC]

The next available general-purpose register. Otherwise, in the next argument on the stack.

General-purpose register %rax

VAX complex double-precision float (D_floating and G_floating) [DC, GC]

The next two available general-purpose registers. Otherwise, in the next two argument slots on the stack.

Registers %rax (the real part of a value) and %rdx (the imaginary part of a value)
IEEE complex single-precision float [FSC]

In the next available XMM register, real part in bits 31:0, imaginary part in bits 63:32. Otherwise, in the next argument slot on the stack.

Register %xmm0, the real part of a value in bits 31:0, the imaginary part in bits 63:32
IEEE complex double-precision float [FTC]

In bits 63:0 of the next two available XMM registers. Otherwise, the next two argument slots on the stack.

Bits 63:0 of registers %xmm0 (the real part of a value) and %xmm1 (the imaginary part of a value)
IEEE complex quadruple-precision float [FXC]In the next four available argument slots on the stack.In a caller-allocated memory buffer whose address is passed as a hidden first argument

An argument that requires two registers is never split so that the first part is in a register and the second part is on the stack. Either both parts are in registers or both parts are on the stack.

For example, a procedure that takes ten integer scalar arguments will find the first six arguments in the general-purpose registers, and the last four on the stack. A procedure that takes ten IEEE double-precision floating-point scalars as arguments will find the first eight arguments in the XMM registers, and the last two on the stack. And, a procedure that takes six integer arguments and eight floating-point arguments, regardless of how the integer and floating-point arguments are intermixed, will find all 14 arguments in registers.

2.4.4.2. Aggregate Argument Types

This section describes how the aggregate argument types are passed to procedures.

First, the argument types are assigned in the appropriate classes and then the registers are allocated for passing them.

The following classes are defined:
  • INTEGER class consists of integral types that fit in one of the general-purpose registers including pointers.

  • SSE class consists of types that fit in a floating-point register.

  • SSEUP class consists of types that fit into a floating-point register and can be passed and returned in the upper bytes of it.

  • X87, X87UP, COMPLEX_X87 classes consist of types that can be returned via the x87 FPU.

  • NO_CLASS is used as initializer in the algorithms. It is used for padding as well as empty structures and unions.

  • MEMORY class consists of types that are passed and returned in memory via the stack.

The size of each argument is rounded up to a quadword (8 bytes). Therefore, the stack will always be 8-byte aligned.

Scalar argument types are classified as shown in Table 2.21.
Table 2.21. Classification of Scalar Components of Aggregate Types

Nominal Type
[OpenVMS Type Code]
(prefix DSC$K_DTYPE_)

Equivalent C/C++ Type(s)Argument Passing Class
Pointer [Q]*INTEGER
Boolean [B, BU]_Bool (bool)
Integers (size ≤ 64 bits) [B, W, L, Q, BU, WU, LU, QU]char, short, int, long (signed and unsigned)
Integers (64 < size ≤ 128 bits) [O, OU]__int128 (signed and unsigned)Split into two 8-byte chunks. Both belong to class INTEGER.
VAX floating-point types (up to 64 bits) [F, D, G] INTEGER
VAX floating-point complex (64 bits) [FC] INTEGER
VAX floating-point complex (128 bits) [DC, GC] Split into two 8-byte chunks. Both belong to class INTEGER.
IEEE binary floating-point types (up to 64 bits) [FS, FT]float, doubleSSE
IEEE extended binary floating-point type (128 bits) [FX]__float128Split into two halves. The first (lower addressed) 64-bits belong to class SSE and the second half to class SSEUP.
IEEE binary floating-point complex (64 bits) [FSC]complex floatTreat as two successive binary floating-point values, each treated as a scalar of half the size (see above).
IEEE binary floating-point complex (128 bits) [FTC]complex double
IEEE binary floating-point complex (256 bits) [FXC]complex long double
Aggregate (structures, records and arrays) and union types are classified as follows:
  1. If the size of an object is larger than eight quadwords (64 bytes), or it contains unaligned fields, it belongs to the MEMORY class.

  2. If a C++ object is non-trivial for the purpose of calls, as specified in the C++ ABI?, it is passed by an invisible reference—that is, the object is replaced in the parameter list by a pointer that has the INTEGER class.?

  3. If the size of the aggregate exceeds a single quadword, each quadword is classified separately. Each quadword is initialized to the NO_CLASS class.

  4. Each field of an object is classified recursively so that always two fields are considered. The two fields are the containing quadword as a whole and the lowest level field components of the quadword, considered in order:
    1. If both classes are equal, this is the resulting class.

    2. If one of the classes is NO_CLASS, the resulting class is the other class.

    3. If one of the classes is MEMORY, the result is the MEMORY class.

    4. If one of the classes is INTEGER, the result is the INTEGER class.

    5. If one of the classes is X87, X87UP, or COMPLEX_X87, the result is the MEMORY class.

    6. Otherwise the result is the SSE class.

  5. Then a post merger cleanup is done:
    1. If one of the classes is MEMORY, the whole argument is passed in memory.

    2. If X87UP is not preceded by X87, the whole argument is passed in memory.

    3. If the size of the aggregate exceeds two quadwords and the first quadword is not SSE or any other quadword is not SSEUP, the whole argument is passed in memory.

    4. If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE.

Once arguments are classified, the registers are assigned (in left-to-right order) for passing as follows:
  1. If the class is MEMORY, the argument is passed on the stack.

  2. If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8, and %r9 is used.

  3. If the class is SSE, the argument is passed in the next available floating-point register. The registers are taken in order from %xmm0 to %xmm7.

  4. If the class is SSEUP, the quadword is passed in the next available 8-byte chunk of the last used floating-point register.

  5. If the class is X87, X87UP, or COMPLEX_X87, the argument is passed in memory.

When a value of a boolean type is returned or passed in a register or on the stack, bit 0 contains the truth value, bits 1 to 7 must be zero, and all other bits are left unspecified. A consumer of such values can rely on it being 0 or 1 only when truncated to the low byte.

If there are no registers available for any quadword of an argument, the whole argument is passed on the stack. If registers have already been assigned for some quadwords of such an argument, the assignments are reverted.

Once registers are assigned, the arguments passed in memory are pushed on the stack in reversed (right-to-left?) order.

Certain arrays of IEEE floating-point components are given special case treatment to take advantage of SSE/AVX floating-point features. These arrays must have both a size and an alignment that is one of 64, 128, 256 or 512 bytes. Multiples of these sizes are also allowed. These are shown in Table 2.22.
Table 2.22. Classification of Special Floating-Point Array Components of Aggregate Types

Nominal Type
[OpenVMS Type Code]
(prefix DSC$K_DTYPE_)

Equivalent C/C++ Type(s)Argument Passing Class
IEEE binary floating-point vector (up to 64 bits) [M64]__m64SSE
IEEE extended binary floating-point vector (128 bits) [M128]__m128Split into two halves. The first (lower addressed) 64-bits belong to class SSE and the second half to class SSEUP.
IEEE binary floating-point vector (256 bits) [M256]__m256Split into four 8-byte chunks. The first chunk belongs to class SSE and the rest to class SSEUP.
IEEE binary floating-point vector (512 bits) [M512]__m512Split into eight 8-byte chunks. The first chunk belongs to class SSE and the rest to class SSEUP.

When passing the __m256 or __m512 arguments to functions that use varargs or stdarg, function prototypes must be provided. Otherwise, the run-time behavior is undefined.

2.4.4.3. Unused Bits in Passed Data

Whenever data is passed by value between two procedures in registers or in memory, the bits not used by the data elements are sign-extended or zero-extended as appropriate to the type. Unsigned integral (except unsigned 32-bit), set, and VAX floating-point values passed in general-purpose registers are zero-extended, while signed integral values as well as unsigned 32-bit integral values are sign-extended to 64 bits. For all other types passed in the general-purpose registers, unused bits are undefined.

Note

Bit 31 is replicated in bits 32—63, even for unsigned 32-bit integers.

This rule applies to the argument types described in Section 2.4.4.1 as well as the individual elements of aggregate types passed in general-purpose registers as described in Section 2.4.4.2.

The rules contained in this section are summarized in Tables 2.23 and 2.24.
Table 2.23. Unused Bits in Passed Data

Data Type
(OpenVMS Names)

Type Designator?

Data Size (bytes)

Register Extension Type

Memory Extension Type

Byte logical

DSC$K_DTYPE_BU

1

Zero64

Zero64

Word logical

DSC$K_DTYPE_WU

2

Zero64

Zero64

Longword logical

DSC$K_DTYPE_LU

4

Sign64

Sign64

Quadword logical

DSC$K_DTYPE_QU

8

Data64

Data64

Byte integer

DSC$K_DTYPE_B

1

Sign64

Sign64

Word integer

DSC$K_DTYPE_W

2

Sign64

Sign64

Longword integer

DSC$K_DTYPE_L

4

Sign64

Sign64

Quadword integer

DSC$K_DTYPE_Q

8

Data64

Data64

F_floating

DSC$K_DTYPE_F

4

VAXF64

Data32

D_floating

DSC$K_DTYPE_D

8

VAXDG64

Data64

G_floating

DSC$K_DTYPE_G

8

VAXDG64

Data64

F_floating complex

DSC$K_DTYPE_FC

2 * 4

2*VAXF64

2*Data32

D_floating complex

DSC$K_DTYPE_DC

2 * 8

2*VAXDG64

2*Data64

G_floating complex

DSC$K_DTYPE_GC

2 * 8

2*VAXDG64

2*Data64

S_floating

DSC$K_DTYPE_FS

4

Hard

Data32

T_floating

DSC$K_DTYPE_FT

8

Hard

Data64

X_floating

DSC$K_DTYPE_FX

16

N/A

N/A

S_floating complex

DSC$K_DTYPE_FSC

2 * 4

Hard?

2*Data32

T_floating complex

DSC$K_DTYPE_FTC

2 * 8

2*Hard

2*Data64

X_floating complex

DSC$K_DTYPE_FXC

2 * 16

N/A

N/A

Small structures of 8 bytes or less

N/A

≤8

Nostd

Nostd

Small arrays of 8 bytes or less

N/A

≤8

Nostd

Nostd

32-bit address

N/A

4

Sign64

Sign64

64-bit address

N/A

8

Data64

Data64

Table 2.24 contains the defined meanings for the extension type symbols used in Table 2.23.
Table 2.24. Extension Type Codes

Sign Extension Type

Defined Function

Sign64

Sign-extended to 64 bits.

Zero64

Zero-extended to 64 bits.

Data32

Data is 32 bits. The state of bits <63:32> is unpredictable.

2*Data32

Two single-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data32).

Data64

Data is 64 bits.

2*Data64

Two double-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data64).

VAXF64

Data is 64 bits. Low-order 32 bits are the same as the F_floating memory format and the high-order 32 bits are zero. (Used only in a general register, never in a floating-point register).

VAXDG64

Data is 64 bits. Uses the corresponding D_floating or G_floating memory format. (Used only in a general register, never in a floating-point register).

2*VAXF64

Two single-precision parts of the complex value are stored in memory as independent floating-point values (each handled as VAXF64).

2*VAXDG64

Two double-precision parts of the complex value are stored in memory as independent floating-point values (each handled as VAXDG64).

Hard

Passed in the layout defined by the hardware SRM.

2*Hard

Two floating-point parts of the complex value are stored in a pair of registers as independent floating-point values (each handled as Hard).

Nostd

State of all high-order bits not occupied by the data is unpredictable across a call or return.

2.4.4.4. Argument Information Register (AI)

On all standard calls, the caller must pass information on the number, location and limited type information of all arguments. The called procedure can use this information in various argument count and argument list built-ins. To support this, %rax is used as the AI register. It must contain the argument information that is presented in Table 2.25.
Table 2.25. Contents of the Argument Information Register (%rax)
BitContents
7:0 (%al)Upper bound on the number of XMM registers that are used to pass arguments
15:8 (%ah)Total number of passed argument slots
47:16Argument Info Offset relative to the return address of the caller, or zero
63:48

Reserved and must be either 0x0000 or 0xFFFF?

If the Argument Info Offset field is non-zero, it contains a signed byte offset to an Argument Info Block (AIB). This byte offset is relative to the return address of the caller, that is, an offset from the location of the instruction after the call instruction. The Argument Info Block must be close enough to the call site for the offset to fit in 32 bits. If the AIB is in the same section as the code, this offset can be calculated at compile time.

Table 2.26 shows the format of an Argument Info Block.
Table 2.26. Argument Info Block Format
BitNameUsage
7:0versionFormat version. This format is version 1.
15:8arg info countNumber of argument slots represented in this block.
19:161st arg infoInformation on the 1st argument slot.
23:202nd arg infoInformation on the 2nd argument slot.
  

.
.
.

  Information on the nth argument slot.

The arg info count may be less than, equal to, or greater than the actual number of passed arguments. If it is less, the missing argument information fields are assumed to be 0 (AI$K_AR_I64). If it is greater, the extra entries in this block are ignored.

If all the passed arguments are integers and pointers, there is no need to pass an Argument Info Block. Instead, the Argument Info Offset should be set to zero.

The values of the argument information fields are shown in Table 2.27.
Table 2.27. Argument Slot Information Values
ValueNameMeaning
0AI$K_AR_I64

Argument is passed in a general-purpose register, if one is available, otherwise on the stack. or Argument is not present.

1AI$K_AR_FFF_floating argument is passed in a general-purpose register.
2AI$K_AR_FDD_floating argument is passed in a general-purpose register.
3AI$K_AR_FGG_floating argument is passed in a general-purpose register.
4AI$K_AR_FSArgument is passed in bits 31:0 of an XMM register.
5AI$K_AR_FTArgument is passed in bits 63:0 of an XMM register.
6AI$K_AR_FXLLow half of argument is passed in bits 63:0 of an XMM register.
7AI$K_AR_FXHHigh half of argument is passed in bits 127:64 of an XMM register.
8AI$K_AR_MEMArgument is pushed on the stack.
9—15Reserved.
Note that the AI$K_AR_FXL and AI$K_AR_FXH argument fields always occur in pairs.

2.4.4.5. Variable Argument Lists

The x86-64 industry standards define how C-style variable argument lists (va_start, va_arg and so on) are implemented. OpenVMS also allows variable argument lists to be accessed as arrays. On prior OpenVMS architectures, a single common mechanism supports both. On OpenVMS x86-64, different mechanisms are implemented.

2.4.4.5.1. Standard Variable Arguments
The x86-64 standard mechanism uses the va_list structure and the register save area. The register save area structure is presented in Table 2.28.
Table 2.28. Register Save Area Structure
OffsetRegisterUsage
0%rdi1st general-purpose argument register
8%rsi2nd general-purpose argument register
16%rdx3rd general-purpose argument register
24%rcx4th general-purpose argument register
32%r85th general-purpose argument register
40%r96th general-purpose argument register
48%xmm01st floating-point argument register
64%xmm12nd floating-point argument register
80%xmm23rd floating-point argument register
96%xmm34th floating-point argument register
112%xmm45th floating-point argument register
128%xmm56th floating-point argument register
144%xmm67th floating-point argument register
160%xmm78th floating-point argument register

The register save area is always allocated in the stack frame of the called function. Any function that contains an invocation of the va_start macro must save argument registers in the register save area. The six general-purpose registers are always saved. The number of floating-point registers to be saved depends on the value passed in the %al register. In theory, code should not save more registers than indicated in %al, but in practice, it either saves none (if %al is zero) or all the registers.

The standard requires the caller to pass a floating-point register argument count in the %al register whenever the called function uses the C variable arguments. This includes not only functions explicitly declared with the variable arguments, but all unprototyped functions as well.

Note that the OpenVMS “arginfo notused” linkage does not influence whether this value is passed in the %al or not. The passed value does not need to be absolutely correct, but should at least be an upper bound on the number of arguments passed in floating-point registers.

The x86-64 va_list structure contains the following fields that are described in Table 2.29.
Table 2.29. va_list Structure
OffsetFieldUsage
0gp_offsetByte offset from the start of the register save area of the next available saved integer argument register
4fp_offsetByte offset from the start of the register save area of the next available saved floating-point argument register
8overflow_arg_areaPointer to the first available stack argument
16reg_save_areaPointer to the register save area
The va_start macro initializes the va_list structure as follows:
  • gp_offset is the byte offset within the register save area of the first unused general-purpose register.

  • fp_offset is the byte offset within the register save area of the first unused floating-point register.

  • overflow_arg_area points to the first unused stack argument.

  • reg_save_area points to the register save area that is already initialized.

For example, for the printf(const char *fmt, ...) function, the va_list structure is initialized as follows:
  • gp_offset is set to +8, the offset of the second general-purpose argument; the first argument (fmt) is already used.

  • fp_offset is set to +48, the offset of the first floating-point argument.

  • overflow_arg_area is set to FP+16, the location of the first stack argument.

When the va_arg macro is invoked, it fetches the argument from a saved register or the stack and increments one field on the va_list structure accordingly. For example, if an integer argument is requested, the va_arg macro will compare the value of gp_offset against 48. If gp_offset is less than 48, the va_arg macro will return a saved integer register and increment gp_offset. Otherwise, it will return a stack argument and increment overflow_arg_area.

2.4.4.5.2. OpenVMS Variable Argument Lists
A number of OpenVMS languages allow a procedure to query the total number of arguments and to access arguments as a single array. The following language constructs allow this:
  • ARGPTR, ACTUALPARAMETER and ACTUALCOUNT in BLISS

  • [list], argument, and argument_list_length in VSI Pascal

  • va_count in VSI C

All rely on OpenVMS extensions to the standard calling conventions.

On OpenVMS standard calls, the caller passes argument information in the %rax register that specifies the total number of the used argument slots and location of each register argument. In theory, this information only needs to be passed if the called procedure uses one of the above mentioned language constructs, but since the caller is not able to determine this, the argument information is passed in %rax on all OpenVMS standard calls. It can be suppressed with the “arginfo notused” linkage specification.

If a called procedure requests its argument count, it is in %ah. If a called procedure requests an argument list, the called procedure performs the following:
  1. Allocates the storage in its own stack frame for the entire arglist (8 * %ah).

  2. Copies all general-purpose registers, floating-point registers, and memory arguments to the arglist as indicated by the values in %rax.

Unlike the prior OpenVMS architectures, on OpenVMS x86-64 it is not possible to create a register “home” on the stack that is contiguous with the incoming memory arguments.

2.5. Argument Passing Mechanisms

Each high-level language supported by OpenVMS provides a mechanism for passing arguments to a procedure. The specifics of the mechanism and the terminology used, however, vary from one language to another. For specific information, refer to the appropriate high-level language user's guide.

OpenVMS system routines are external procedures that accept arguments. The argument list contains the parameters that are passed to the routine. Depending on the passing mechanisms for these parameters, the forms of the arguments contained in the argument list vary. As shown in Figures Figure 2.14 and Figure 2.15, argument entries labeled arg1 through argn are the actual parameters, which can be any of the following addresses or value:
  • An uninterpreted 32-bit value on VAX or 64-bit value on Alpha and I64 systems is passed by value.

  • An address of a data value is passed by reference.

  • An address of a descriptor that contains a pointer to a data value is passed by descriptor (for example, a string might be the data value).

Figure 2.14. Alpha Procedure Argument-Passing Mechanisms
Alpha Procedure Argument-Passing Mechanisms
Figure 2.15. VAX Procedure Argument-Passing Mechanisms
VAX Procedure Argument-Passing Mechanisms

OpenVMS programming reference manuals provide a description of each OpenVMS system routine that indicates how each argument is to be passed. Phrases such as an address and address of a character string descriptor identify reference and descriptor arguments, respectively. Terms like Boolean value, number, value, and mask indicate an argument that is passed by value.

2.5.1. Passing Arguments by Value

When your program passes an argument using the by value mechanism, the argument list entry contains either the actual uninterpreted 32-bit VAX value or a 64-bit Alpha or I64 value (zero- or sign-extended) of the argument. For example, to pass the constant 100 by value, the calling program puts 100 directly in the argument list or sequence. For more information about passing 64-bit Alpha and I64 values, refer to VSI OpenVMS Programming Concepts Manual, Volume I.

All high-level languages (except C) require you to specify the by-value mechanism explicitly when you call a procedure that accepts an argument by value. For example, FORTRAN uses the %VAL built-in function, while COBOL uses the BY VALUE qualifier on the CALL[USING] statement.

A FORTRAN program calls a procedure using the by-value mechanism as follows:
     INCLUDE  '($SSDEF)'
     CALL LIB$STOP (%VAL(SS$_INTOVF))
A BLISS program calls this procedure as follows:
     LIB$SIGNAL (SS$_INTOVF)
The equivalent VAX MACRO code is as follows:
     PUSHL    #SS$_INTOVF        ; Push longword by value
     CALLS    #1,G^LIB$SIGNAL    ; Call LIB$SIGNAL
A C language program calls a procedure using the by-value mechanism as follows:
 #include <starlet.h>         /* Declare the function*/
       .
       .
     enum  cluster0
        {
           completion, breakdown, beginning
        }  event;

     int status;
     event = completion;
       .
       .
     status = sys$setef(event);     /* Set event flag */

2.5.2. Passing Arguments by Reference

When your program passes arguments using the by reference mechanism, the argument list entry contains the address of the location that contains the value of the argument. For example, if variable x is allocated at location 1000, the argument list entry will contain 1000, the address of the value of x.

On Alpha processors and I64, the address is sign-extended from 32 bits to 64 bits.

Most languages (but not C) pass scalar data by reference by default. Therefore, if you simply specify x in the CALL statement or function invocation, the language automatically passes the value stored at the location allocated to x to the OpenVMS system routine.

A VAX BLISS program calls a procedure using the by-reference mechanism as follows:
    LIB$FLT_UNDER (%REF(1))
The equivalent VAX MACRO code is as follows:
ONE:     .LONG    1                     ; Longword value 1
           .
           .
           .
         PUSHAL    ONE                  ; Push address of longword
         CALLS    #1,G^LIB$FLT_UNDER    ; Call LIB$FLT_UNDER
A C language program calls a procedure using the by-reference mechanism as follows:
 /*  This program shows how to call system service SYS$READEF.  */

 #include <ssdef.h>
 #include <stdio.h>

 #include <starlet.h>         /*  Declare the function  */

 main(void)
 {
                               /*  Longword that receives the status *
                                *  of the event flag cluster         */
   unsigned cluster_status;

   int return_status;          /*  Status: SYS$READEF  */

                               /*  Argument values for SYS$READEF  */
   enum  cluster0
      {
         completion, breakdown, beginning
      }  event;
      .
      .
      .
   event = completion;           /*  Event flag in cluster 0  */

                                 /*  Obtain status of cluster 0.  *
                                  *  Pass value of event and      *
                                  *  address of cluster_status.   */

   return_status =  SYS$READEF(event, &cluster_status);

                                   /*  Check for successful call  */
   if (return_status != SS$WASCLR && return_status != SS$WASSSET)
      {
         /* Handle the error here.  */
            .
            .
            .
      }
   else
      {
         /*  Check bits of interest in cluster_status here.  */
            .
            .
            .
      }
  }

2.5.3. Passing Arguments by Descriptor

When a procedure specifies that an argument is passed by descriptor, the argument list entry must contain the address of a descriptor for the argument. For more information about OpenVMS Alpha 64-bit descriptors, refer to VSI OpenVMS Programming Concepts Manual, Volume I.

On Alpha and I64 processors, the address is sign-extended from 32 bits to 64 bits.

This mechanism is used to pass more complicated data. For both Alpha and VAX systems, a descriptor includes at least the following fields:

Symbol

Description

DSC$W_LENGTH

Length of data (or DSC$W_MAXSTRLEN, maximum length, for varying strings)

DSC$B_DTYPE

Data type

DSC$B_CLASS

Descriptor class code

DSC$A_POINTER

Address at which the data begins

The VSI OpenVMS Calling Standard describes these fields in greater detail.

OpenVMS high-level languages include extensions for passing arguments by descriptor. When you specify by descriptor in these languages, the compiler creates the descriptor, defines its fields, and passes the address of the descriptor to the OpenVMS system routine. In some languages, by descriptor is the default passing mechanism for certain types of arguments, such as character strings. For example, the default mechanism for passing strings in BASIC is by descriptor.
    100    COMMON STRING GREETING = 30
    200    CALL LIB$PUT_SCREEN(GREETING)
The default mechanism for passing strings in COBOL, however, is by reference. Therefore, when passing a string argument to an OpenVMS system routine from a COBOL program, you must specify BY DESCRIPTOR for the string argument in the CALL statement.
    CALL LIB$PUT_OUTPUT USING BY DESCRIPTOR GREETING
In VAX MACRO or BLISS, you must define the descriptor's fields explicitly and push its address onto the stack. Following is the VAX MACRO code that corresponds to the previous examples.
MSGDSC:    .WORD LEN                     ; DESCRIPTOR:  DSC$W_LENGTH
           .BYTE DSC$K_DTYPE_T           ; DSC$B_DTYPE
           .BYTE DSC$K_CLASS_S           ; DSC$B_CLASS
           .ADDRESS MSG                  ; DSC$A_POINTER

MSG:       .ASCII/Hello/                 ; String itself
LEN = .-MSG                              ; Define the length of the string

            .ENTRY  EX1,^M<>
            PUSHAQ MSGDSC                ; Push address of descriptor
            CALLS #1,G^LIB$PUT_OUTPUT    ; Output the string
            RET
            .END EX1
The equivalent BLISS code looks like this:
MODULE BLISS1 (MAIN = BLISS1,    ! Example of calling LIB$PUT_OUTPUT
        IDENT = '1-001',
        ADDRESSING_MODE(EXTERNAL = GENERAL)) =
BEGIN
EXTERNAL ROUTINE
    LIB$STOP,                   ! Stop execution via signaling
    LIB$PUT_OUTPUT;             ! Put a line to SYS$OUTPUT

FORWARD ROUTINE
    BLISS1 : NOVALUE;

LIBRARY 'SYS$LIBRARY:STARLET.L32';

ROUTINE BLISS1                  ! Routine
        : NOVALUE =

    BEGIN
!+
! Allocate the necessary local storage.
!-
    LOCAL
        STATUS,                         ! Return status
        MSG_DESC : BLOCK [8, BYTE];     ! Message descriptor

    BIND
        MSG = UPLIT('HELLO');

!+
! Initialize the string descriptor.
!-
    MSG_DESC [DSC$B_CLASS] = DSC$K_CLASS_S;
    MSG_DESC [DSC$B_DTYPE] = DSC$K_DTYPE_T;
    MSG_DESC [DSC$W_LENGTH] = 5;
    MSG_DESC [DSC$A_POINTER] = MSG;
!+
! Put out the string.  Test the return status.
! If it is not a success, then signal the RMS error.
!-
    STATUS = LIB$PUT_OUTPUT(MSG_DESC);
    IF NOT .STATUS THEN LIB$STOP(.STATUS);
    END;                ! End of routine BLISS1
END                     ! End of module BLISS1
ELUDOM
A C language program calls a procedure using the by-descriptor mechanism as follows:
 /*  This program shows a call to system service SYS$SETPRN.  */

 #include <ssdef.h>
 #include <stdio.h>
                              /*  Define structures for descriptors  */
 #include <descrip.h>

 #include starlet.h           /*  Declare the function  */

 int main(void)
 {
   int  ret;                  /*  Define return status of SYS$SETPRN  */

   struct  dsc$descriptor_s  name_desc;  /* Name the descriptor */

   char  *name =  "NEWPROC";             /* Define new process name */
      .
      .
      .
   name_desc.dsc$w_length = strlen(name);  /* Length of name without *
                                            * null terminator        */

   name_desc.dsc$a_pointer =  name; /* Put address of shortened string *
                                     * in descriptor             */

   name_desc.dsc$b_class =  DSC$K_CLASS_S; /* String descriptor class */

   name_desc.dsc$b_dtype =  DSC$K_DTYPE_T; /* Data type: ASCII string */
      .
      .
      .
   ret =  sys$setprn(&name_desc);

   if (ret != SS$_NORMAL)                  /*  Test return status  */
      fprintf(stderr, "Failed to set process name\n"),
      exit(ret);
      .
      .
      .
 }

2.5.4. Passing Scalars as Arguments

When you are passing an input scalar value to an OpenVMS system routine, you usually pass it either by reference or by value. You usually pass output scalar arguments by reference to OpenVMS system routines. An output scalar argument is the address of a location where some scalar output of the routine will be stored.

2.5.5. Passing Arrays as Arguments

Arrays are passed to OpenVMS system routines by reference or by descriptor.

Sometimes the routine knows the length and dimensions of the array to be received, as in the case of the table passed to LIB$CRC_TABLE. Arrays such as this are normally passed by reference.

In other cases, the routine actually analyzes and operates on the input array. The routine does not necessarily know the length or dimensions of such an input array, so a descriptor is necessary to provide the information the routine needs to describe the array accurately.

2.5.6. Passing Strings as Arguments

Strings are passed by descriptor to OpenVMS system routines. Table 2.30 lists the string-passing descriptors recognized by a system routine.
Table 2.30. String-Passing Descriptors

Descriptor Function

Descriptor Class Code

Numeric Value

Fixed length (string/scalar)

DSC$K_CLASS_S

1

Dynamic

DSC$K_CLASS_D

2

Array

DSC$K_CLASS_A

4

Scaled decimal

DSC$K_CLASS_SD

9

Noncontiguous array

DSC$K_CLASS_NCA

10

Varying length

DSC$K_CLASS_VS

11

An OpenVMS system routine writes strings according to the following types of semantics:
  • Fixed length — Characterized by an address and a constant length

  • Varying length — Characterized by an address, a current length, and a maximum length

  • Dynamic — Characterized by a current address and a current length

2.6. Combinations of Descriptor Class and Data Type

Some combinations of descriptor class and data type are not permitted, either because they are not meaningful or because the calling standard does not recognize them. Possibly, the same function can be performed with more than one combination. This section describes the restrictions on the combinations of descriptor classes and data types. These restrictions help to keep procedure interfaces simple by allowing a procedure to accept a limited set of argument formats without sacrificing functional flexibility.

The tables in Figures 2.16, 2.17, and 2.18 show all possible combinations of descriptor classes and data types. For example, Figure 2.16 shows that your program can pass an argument to an OpenVMS system routine whose descriptor class is DSC$K_CLASS_A (array descriptor) and whose data type is unsigned byte (DSC$K_DTYPE_BU). The calling standard does not permit your program to pass an argument whose descriptor class is DSC$K_CLASS_D (dynamic string) and whose data type is unsigned byte.

A descriptor with data type DSC$K_DTYPE_DSC (24) points to a descriptor that has class DSC$K_CLASS_D (2) and data type DSC$K_DTYPE_T (14). All other class and data type combinations in the target descriptor are reserved for future definition in the standard.

The scale factor for DSC$K_CLASS_SD is always a decimal data type. It does not vary with the data type of the data described by the descriptor.

For DSC$K_CLASS_UBS and DSC$K_CLASS_UBA, the length field specifies the length of the data field in bits. For example, if the data type is unsigned word (DSC$K_DTYPE_WU), DSC$W_LENGTH equals 16.

Figure 2.16. Atomic Data Types and Descriptor Classes
Atomic Data Types and Descriptor Classes
Figure 2.17. String Data Types and Descriptor Classes
String Data Types and Descriptor Classes
Figure 2.18. Miscellaneous Data Types and Descriptor Classes
Miscellaneous Data Types and Descriptor Classes

2.7. Function Value Return

A function is a routine that returns a single value to the calling routine. The function value represents the value of the expression in the return statement. As specified by the calling standard, a function value may be returned as an actual value in R0.

2.7.1. Return Values on OpenVMS VAX

On OpenVMS VAX systems, if the actual function value returned is greater than 32 bits, then both R0 and R1 should be used.

2.7.2. Return Values on OpenVMS Alpha

On OpenVMS Alpha systems, if the actual function returned is a floating-point value, the floating-point value is returned either in F0 or in both F0 and F1.

A standard function must return its function value by one of the following mechanisms:
  • Immediate value

  • Reference

  • Descriptor

These mechanisms are the standard return convention because they support the language-independent data types. For information about condition values returned in R0, see Section 2.8.

2.7.3. Return Values on OpenVMS I64

On OpenVMS I64 systems, values up to 128 bits are returned directly in the registers, according to the rules in Table 2.31.

Integer, enumeration, record, and set values (bit vectors) smaller than 64 bits must be zero-filled (unsigned integers, enumerations, records, sets) or sign-extended (signed integrals) to a full 64 bits. However, for unsigned 32-bit integers, bit 31 is replicated in bits 32—63.

When floating-point values are returned in floating-point registers, they are returned in the register format, rounded to the appropriate precision. When they are returned in the general registers (for example, as part of a record), they are returned in their memory format.

OpenVMS does not support a general notion of homogeneous floating-point aggregates. However, the special case of two single-precision or double-precision floating-point values implementing values of a complex type are handled in an analogous manner.
Table 2.31. Rules for Return Values

Type

Size (Bits)

Location of Return Value

Alignment

Integer/Pointer, small Record, Set

1—64

R8

LSB

IEEE single-precision floating-point (S_floating)

32

F8

N/A

IEEE double-precision floating-point (T_floating)

64

F8

N/A

IEEE single-precision complex (S_floating)

64

F8, F9

N/A

IEEE double-precision complex (T_floating)

128

F8, F9

N/A

VAX single-precision floating-point (F_floating)

32

R8

N/A

VAX double-precision floating-point (D_ and G_floating)

64

R8

N/A

VAX single-precision floating-point complex (F_floating)

64

R8, R9

N/A

VAX double-precision floating-point complex (D_ and G_floating)

128

R8, R9

N/A


Note

X_floating and X_floating complex are not included in this table because they are returned using he hidden parameter method.

The rules in Table 2.31 are expressed in more detail in Table 2.17. F_floating and F_floating complex values in the general registers are zero-extended (Zero64), because this most closely approximates the effect of using the Alpha register format.

Hidden Parameter

Return values other than those covered by Table 2.31 are returned in a buffer allocated by the caller. A pointer to the buffer is passed to the called procedure as a hidden first parameter, and all normal parameters are shifted one slot to make this possible. The return buffer must be aligned at a 16-byte boundary.

2.7.4. Return Values on OpenVMS x86-64

On OpenVMS x86-64 systems, procedure return values are classified and returned to the appropriate locations depending on their classes as defined for arguments in Section 2.4.4.2.
  1. If the class is MEMORY, then the caller provides the space for the return value and passes the address of this storage in %rdi as if it were the first argument to the function. In effect, this address becomes a hidden first argument. This storage must not overlap any data visible to the callee through the other parameters in this argument list.

    On return %rax will contain the address that was passed in %rdi by the caller.

  2. If the class is INTEGER, the next available register of the sequence %rax, %rdx is used.

  3. If the class is SSE, the next available floating-point register of the sequence %xmm0, %xmm1 is used.

  4. If the class is SSEUP, the quadword is returned in the next available 8-byte chunk of the last used floating-point register.

  5. If the class is X87, the value is returned on the X87 stack in %st0 as an 80-bit x87 number.

  6. If the class is X87UP, the value is returned together with the previous X87 value in %st0.

  7. If the class is COMPLEX_X87, the real part of the value is returned in %st0 and the imaginary part in %st1.

As a result scalar values and complex floating-point values are returned in registers %rax, %rax and %rdi, %mm0, or %mm0 and %mm1. The exception is an IEEE complex quadruple precision value which is returned in a caller-provided temporary location.

2.8. Condition Value Return

An OpenVMS system routine can indicate success or failure to the calling program by returning a condition value. In addition, an error condition to the calling program can return as a condition value in R0 (R8, R9 for I64) or by error signaling.

A condition value in R0 (R8, R9 for I64), also called a return status or completion code, is either a success (bit 0 = 1) value or an error condition (bit 0 = 0) value. In an error condition value, the low-order 3 bits specify the severity of the error (see Figure 2.19). Bits <27:16> contain the facility number, and bits <15:3> indicate the particular condition. Bits <31:28> are the control field. When the called procedure returns a condition value, the calling program can test R0 and choose a recovery path. A general guideline to follow when testing for success or failure is that all success codes have odd values and all error codes have even values.

Figure 2.19. Format of a Condition Value
Format of a Condition Value

When the completion code is signaled, the calling program must establish a handler to get control and take appropriate action. (See VSI OpenVMS Programming Concepts Manual, Volume I or the VSI OpenVMS Calling Standard for a description of signaling and condition handling and for more information about the condition value).

2.9. MACRO Compiler Register Mapping

2.9.1. MACRO Register Usage and Mapping for I64

Because the I64 calling standard diverges from the Alpha and VAX calling standards regarding the use of registers and register mapping, and because Macro-32 assumes that registers are preserved across calls, the MACRO compiler maps registers to allow existing code to compile unmodified.

If you use OpenVMS high-level languages, the register and register mapping differences in the calling standards are handled by the compilers and are not exposed to your code. However, if your code uses Macro-32, C #pragmalinkages, or BLISS linkages, your code might have to take into account the differences in register mapping.

This section describes I64 register usage and mapping.

2.9.1.1. I64 Register Usage Compared with Alpha and VAX

OpenVMS I64 systems employ 32 integer registers, R0 through R31, with R0 being a read-only register that contains 0. This is different from OpenVMS Alpha, where R31 is a read-write register that contains 0.

In addition, the I64 calling standard has been written to be highly compatible with the Intel calling standard, and is quite different from the OpenVMS Alpha calling standard. For example, the standard return registers on I64 are R8/R9, not R0/R1 as on Alpha. The I64 calling standard reserves R1 as the GP (global pointer), does not include a standardized FP (frame pointer), and only has R4 through R7 as preserved across calls, not R2 through R15 as on Alpha.

I64 register usage differs from that of Alpha and VAX in the following key ways:
  • Registers 2 through 11 are preserved on OpenVMS VAX

  • Registers 2 through 15 are preserved on OpenVMS Alpha

  • Registers 4 through 7 are preserved on OpenVMS I64

  • I64 has more volatile registers

  • I64 returns values in R8/R9 instead of R0/R1

  • R0 is read only in I64

  • I64 reserves R1 as the GP (global pointer)

  • I64 does not include a standardized FP (frame pointer)

  • Arguments are also passed in stacked registers in I64. R32—R39 are used as incoming argument registers.

2.9.1.1.1. I64 Register Mapping in MACRO Compiler

The OpenVMS MACRO compiler compiles Macro-32 source code written for OpenVMS VAX systems (the VAX MACRO assembler) into machine code that runs on OpenVMS Alpha and OpenVMS I64 systems. Because Macro-32 source code is written with the VAX and Alpha calling standards in mind, the compiler performs several transformations to allow existing code to compile unmodified with the I64 compiler.

The MACRO compiler maps the registers in Macro-32 source programs to I64 registers on your behalf, as shown in Table 2.32, to minimize source changes. This allows existing programs to use MOVL SS$_NORMAL, R0and have the generated code return the value in R8 as prescribed by the calling standard. The mapping to an actual I64 register is totally transparent to the Macro-32 source code (and most of the compiler).
Table 2.32. Register Mapping Table for OpenVMS VAX/OpenVMS Alpha to OpenVMS I64

OpenVMS VAX/OpenVMS Alpha Register in Source Code

OpenVMS I64 Register Used in Generated Code

R0

R8

R1

R9

R2

R28

R3

R3

R4

R4

R5

R5

R6

R6

R7

R7

R8

R26

R9

R27

R10

R10

R11

R11

R12

R30

R13

R31

R14

R20

R15

R21

R16

R14

R17

R15

R18

R16

R19

R17

R20

R18

R21

R19

R22

R22

R23

R23

R24

R24

R25

R25

R26

Itanium stacked register

R27

Itanium stacked register

R28

Itanium stacked register

R29

R29

R30

R12

R31

R0

The register mapping was carefully chosen based on which registers were preserved across calls, which registers may be modified across calls, and which registers are volatile and do not even survive into or out of a call.

As on Alpha, Macro-32 references to AP are mapped by the compiler to the appropriate location depending on whether the arguments have been saved to the stack. To support references to FP, the compiler creates an FP value where needed. The compiler supports references to 0 (FP) to establish condition handlers just like on VAX and Alpha.

The compiler does not provide any syntax for accessing I64 registers directly without going through the mapping table.

The automatic register mapping done by the compiler allows many Macro-32 programs (including those that access Alpha registers R16—R31) to compile without modifications.

Note, however, that use of registers R16—R21 as routine parameters on Alpha is not portable to I64. Use PUSHL to pass parameters to a CALL, and use 4(AP), 8(AP), and so forth in the called routine to refer to them. The compiler will generate the correct register references instead of the stack references implied by the VAX operands.

On I64 systems, the compiler continues to recognize many of the EVAX_* built-ins that provide direct access to Alpha instructions on Alpha systems. These built-ins will generate one or more I64 instructions to perform the same logical operation. See the VSI OpenVMS MACRO Compiler Porting and User's Guide for a complete list of which EVAX_* built-ins are also supported on I64.

2.9.1.1.2. Use of MACRO Linkage Directives to Preserve Registers

For I64 systems, add linkage directives (.CALL_LINKAGE,.DEFINE_LINKAGE, or .USE_LINKAGE) to mark VAX CALLS or CALLG instructions that call routines that return values in registers other than R0 or R1, or to JSB to routines written in a language other than Macro-32. These directives look similar to the .CALL_ENTRY directive and specify input, output, preserved, and scratch masks. In addition, they also have a language keyword to provide an alternative quick specification.

The .CALL_LINKAGE directive associates a named or anonymous linkage with a routine name. When the compiler sees a CALLS, CALLG, JSB, BSBB, or BSBW instruction with the routine name as the target, it will use the associated linkage to decide which registers need to be saved and restored around the call.

The .USE_LINKAGE directive establishes a temporary named or anonymous linkage that will be used by the compiler for the next CALLS, CALLG, JSB, BSBB, or BSBW instruction processed in lexical order. This directive is used when the target of the next CALLS, CALLG, JSB, BSBB, or BSBW instruction is not a name, but a run-time value (for example, CALLS #0, (R6)). When the compiler sees the next CALLS, CALLG, JSB, BSBB, or BSBW instruction, it will use the associated linkage to decide which registers need to be saved and restored around the call. After the instruction is processed, the temporary linkage is reset to null.

The .DEFINE_LINKAGE directive defines a named linkage that can be used with subsequent .CALL_LINKAGE or .USE_LINKAGE directives.

If your Macro-32 code uses a CALLS or CALLG instruction to access routines that return values in registers other than R0 or R1, the contents of the saved and restored registers may not be what you expect. Existing Macro-32 code traditionally assumes that registers R2—R11 and R15 are preserved and returned across calls. For CALLS and CALLG instructions, the MACRO compiler automatically saves and restores registers R2—R3 and R8—R15 in case the target of the call is not Macro-32. However, this means that changes made to these registers by the routine call are undone. This can cause problems if the routine return values were in registers other than R0—R1.

In the following example, m1.mar saves and preserve registers R2, R3, and R9 and undoes the changes made to these registers by the routine call.
M1.mar
   calls #3,g^body_scan

M2.mar
   Body_scan:
   .call_entry preserve=<r6,r7,r8>, output=<r2,r3,r4,r5,r9>
To avoid this problem, add a .CALL_LINKAGE directive to m1.mar (or to a common prefix file or macro):
.call_linkage rtn_name=body_scan preserve=<r6,r7,r8> -
                                 output=<r2,r3,r4,r5,r9>

For JSB instructions, the MACRO compiler assumes that the target is also Macro-32 and does not save and restore anything. The compiler assumes that all registers flow in and out of the target routine. Alpha high-level language compilers would have preserved registers R2—R15. However, I64 high-level language compilers preserve only registers R4—R7.

In the following example m1.mar assumes that registers R0—R15 are returned or preserved by the target BLISS routine. On Alpha, BLISS would have done that. On I64, it preserves only registers R4—R7:
.call_linkage rtn_name=body_scan preserve=<r6,r7,r8> -
                                 output=<r2,r3,r4,r5,r9>
To avoid this problem, add a .CALL_LINKAGE directive to m1.mar:
M1.mar
   jsb search_path

M2.bli
   linkage l = jsb(register=0) : global(wrk=10,prc=11)
   global routine search_path : l = begin . . . End;
Indirect calls with mismatched registers are not detected by the linker since it does not know what routine is being called. An indirect JSB to a BLISS or C routine requires a .USE_LINKAGE directive:
  .call_linkage rtn_name=search_path language=other -
                                     output=<r10,r11>
If the routine returns a register other than R0/R1:
  .use_linkage language=other
  jsb (r5)

See the VSI OpenVMS MACRO Compiler Porting and User's Guide for additional information.

2.9.2. MACRO Register Usage and Mapping for x86-64

2.9.3. High-Level Language Compiler Register Mapping

If you use OpenVMS high-level languages, the register and register mapping differences in the calling standards are handled by the compilers and are not exposed to your code. However, if your code uses C #pragma linkages or BLISS linkages to interface with Macro-32 source code, your code might have to take into account the differences in register mapping.

BLISS added a new qualifier and source level switch to enable register mapping for register numbers in linkage and register declarations. It is off by default. BLISS also has additional support for linkages that reference arguments. The C compiler changed the #pragma linkage to map the registers by default, along with additional support for linkages that reference arguments or floating registers. There are new pragmas to get unmapped linkages.

See your compiler documentation for additional information.

Chapter 3. Calling Run-Time Library Routines

The OpenVMS Run-Time Library is a set of language-independent routines that establish a common run-time environment for user programs. The procedures ensure correct operation of complex language features and help enforce consistent operations on data across languages.

The VSI OpenVMS Calling Standard describes the mechanisms used by OpenVMS languages for invoking routines and passing data between them. In effect, this standard describes the interface between your program and the run-time library routines that your program calls. This chapter describes the basic methods for coding calls to run-time library routines from an OpenVMS common language.

3.1. Overview

When you call a run-time library routine from your program, you must furnish whatever arguments the routine requires. When the routine completes execution, in most cases it returns control to your program. If the routine returns a status code, your program should check the value of the code to determine whether or not the routine completed successfully. If the return status indicates an error, you may want to change the flow of execution of your program to handle the error before returning control to your program.

When you log in, the operating system creates a process that exists until you log out. When you run a program, the system activates an executable image in your process. This image consists of a set of user procedures.

From the run-time library's point of view, user procedures are procedures that exist outside the run-time library and that can call run-time library routines. When you write a program that calls a run-time library routine, the run-time library views your program as a user procedure. User procedures also can call other user procedures that are either supplied by VSI or written by you. Because an OpenVMS native-mode language compiler program exists outside the run-time library, compiler-generated programs that call any run-time library routine are also defined as a set of user procedures.

The main program, or main procedure, is the first user procedure that the system calls after calling a number of initialization procedures. A user program consists of the main program and all of the other user procedures that it calls.

Figure 3.1 shows the calling relationships among a main program, other user procedures, library routines, and the operating system. In this figure, Call indicates that the calling procedures requested some information or action; Return indicates that the called procedure returned the information to the calling procedure or performed the action.

Figure 3.1. Calling the Run-Time Library
Calling the Run-Time Library
Although library routines can always call either other library routines or the operating system, they can call user procedures only in the following cases:
  • When a user procedure establishes its own condition handler. For example, LIB$SIGNAL operates by searching for and calling user procedures that have been established as condition handlers (see the VSI OpenVMS RTL Library (LIB$) Manual for more information).

  • When a user procedure passes to a routine the address of another procedure that the library will call later. For example, when your program calls LIB$SHOW_TIMER, you can pass the address of an action routine that LIB$SHOW_TIMER will call to process timing statistics.

3.2. Call Instructions

Each run-time library routine requires a specific calling sequence. This calling sequence indicates the elements that you must include when calling the routine, and the order of those elements. The form of a calling sequence first specifies the type of call being made. A library routine can be invoked either by a CALL instruction or possibly by a JSB instruction (for VAX systems only) as follows:
  • CALL — Call procedure from a high-level language

  • CALLS — Call procedure with stack argument list instruction (VAX MACRO)

  • CALLG — Call procedure with general argument list instruction (VAX MACRO)

  • JSB — Jump to subroutine instruction (for VAX systems only)

  • JSR — Jump to subroutine instruction (MACRO-64)

On VAX systems, the following restrictions apply to the different types of calls:
  • High-level languages do not differentiate between CALLS and CALLG. They use a CALL statement or a function reference to invoke a run-time library routine.

  • VAX MACRO does not differentiate between functions and subroutines in its CALLS and CALLG instructions.

  • Only VAX MACRO and BLISS programs on VAX systems can explicitly access the JSB entry points that are provided for some routines in the run-time library. You cannot write a program to access the JSB entry points directly from a high-level language.

3.2.1. Facility Prefix and Routine Name

Each routine is identified by a unique entry point name consisting of the facility prefix (for example, MTH$) and the procedure name (for example, MTH$SIN). Run-time library entry points follow the OpenVMS conventions for naming global symbols. A global entry point takes the following general form:
fac$symbol
The elements that make up this format represent the following:

fac

A 2- or 3-character facility name

symbol

A 1- to 27-character symbol

The facility names are maintained in a systemwide registry. A unique, 12-bit facility number is assigned to each facility name for use in (1) condition value symbols, and (2) condition values in procedure return status codes, signaled conditions, and messages. The high-order bit of this number is 0 for facilities assigned by VSI and 1 for those assigned by Application Project Services (APS) and customers. For further information, refer to the VSI OpenVMS Calling Standard.

The run-time library facility names are as follows:

CVT$

Convert routines

DTK$

DECtalk routines

LIB$

Library routines

MTH$

Mathematics routines

OTS$

General-purpose routines

PPL$

Parallel processing routines

SMG$

Screen management routines

STR$

String-handling routines

3.2.2. The RTL Call Entry

Arguments passed to a routine must be listed in your call entry in the order shown in the format section of the routine description. Each argument has four characteristics: OpenVMS usage, data type, access type, and passing mechanism. These characteristics are described in Chapter 1.

Some arguments are optional. Optional arguments are indicated by brackets in the routine descriptions. When your program invokes a run-time library routine using a CALL entry point, you can omit optional arguments at the end of the argument list. If the optional argument is not the last argument in the list, you must either pass a zero by value or use a comma to indicate the place of the omitted argument. Some languages, such as C, require that you pass zero by value for trailing optional arguments. See your language processor documentation for further information.

On VAX systems, the calling program passes an argument list of longwords to a called routine; each longword in the argument list specifies a single argument. Note that a 64-bit floating-point argument would count as 2 longword arguments in the list.

On Alpha systems, the calling program passes arguments in an argument item sequence; each quadword in the sequence specifies a single argument item. Note that the argument item sequence is formed using R16–21 or F16–21 (a register for each argument). The argument item sequence can have a mix of integer and floating-point items that use both register types but must not repeat the same number.

For I64, parameters are passed in a combination of general registers, floating-point registers, and memory, as illustrated in Figure 2.12.The first eight parameters are passed in R32 through R39, with the parameter count in R25 and subsequent parameters in quadwords on the stack.

In the Alpha, VAX, and I64 environments, the called routine interprets each argument using one of three standard passing mechanisms: by value, by reference, or by descriptor. For more information on arguments, see Section 2.4 and Section 2.5.

Optional arguments apply only to the CALL entry points. For example, the call format for a procedure with two optional arguments is as follows:
LIB$GET_INPUT  get-str [,prompt-str] [,out-len]
A FORTRAN program could include any one of the following calls to this procedure:
   INTEGER*4 STAT
    .
    .
    .
   STAT = LIB$GET_INPUT (GET_STR,PROMPT,LENGTH)

   STAT = LIB$GET_INPUT (GET_STR,PROMPT)

   STAT = LIB$GET_INPUT (GET_STR,PROMPT,)

   STAT = LIB$GET_INPUT (GET_STR,,LENGTH)

   STAT = LIB$GET_INPUT (GET_STR)

   STAT = LIB$GET_INPUT (GET_STR,)

   STAT = LIB$GET_INPUT (GET_STR,%VAL(0))

The following examples illustrate the standard mechanism for calling an external procedure, subroutine, or function in most high-level languages.

BASIC

    CALL LIB$MOVTC(SRC, FILL, TABLE, DEST)

    STATUS = LIB$GET_INPUT(STRING, 'NAME:')

BLISS

    LOCAL
        MSG_DESC : BLOCK [8,BYTE];

    MSG_DESC [DSC$B_CLASS] = DSC$K_CLASS_S;
    MSG_DESC [DSC$B_DTYPE] = DSC$K_DTYPE_T;
    MSG_DESC [DSC$W_LENGTH] = 5;
    MSG_DESC [DSC$A_POINTER] = MSG;

    STATUS = LIB$PUT_OUTPUT(MSG_DESC);

C

 #include <lib$routines.h>
 #include <descrip.h>

     $DESCRIPTOR(name, "Name:");
     struct dsc$descriptor_s string:
        .
        .
        .
      status = lib$get_input(&string, &name);

COBOL

    CALL LIB$MOVTC USING BY DESCRIPTOR
        SRC,
        FILL,
        TABLE,
        DEST,
        GIVING RET-STATUS.

FORTRAN

    CALL LIB$MOVTC(SRC, FILL, TABLE, DEST)

    STATUS = LIB$GET_INPUT(STRING, 'NAME:')

Pascal

    RET_STATUS := LIB$MOVTC (SRC, FILL, TABLE, DEST);

PL/I

    CALL LIB$MOVTC(SRC, FILL, TABLE, DEST);

    STATUS = LIB$GET_INPUT(STRING, 'NAME:');

VAX MACRO

In VAX MACRO, a calling sequence takes one of three forms, as illustrated by the following examples:
    CALLS     #2,G^LIB$GET_INPUT

    CALLG     ARGLIST, G^LIB$GET_VM

    JSB       G^MTH$SIN_R4

As these examples show, high-level languages use different forms of the call statement. Each language's user guide gives specific information about calling the run-time library from that language.

3.2.2.1. JSB Call Entries (VAX Only)

On VAX systems, JSB entry point names follow the naming conventions explained in Section 3.2.1, except that they include a suffix indicating the number of the highest register accessed or modified. This suffix helps ensure that the calling program and the called routine agree on the number of registers that the called routine is going to change.

The following example illustrates the VAX MACRO code that invokes the library routine MTH$SIN_R4 by means of a JSB instruction. As indicated in the JSB entry point name, this routine uses R0 through R4.
    JSB G^MTH$SIN_R4    ;F_floating sine uses R0 through R4

JSB entry points are available only to VAX MACRO and VAX BLISS programs. No VAX high-level language provides a mechanism for accessing JSB entry points.

3.2.3. Returns from an RTL Routine

On VAX systems, some run-time library routines return a function value. Typically on a VAX system, the return is in the form of a 32-bit value in register R0 or a 64-bit value in registers R0 and R1. In high-level languages, statuses or function return values in R0 appear as the function result. When a routine returns a function value in R0, it cannot also use R1 to return a status code. Therefore, such a procedure signals errors rather than returning a status. For more information, refer to the VSI OpenVMS Calling Standard or the description of LIB$SIGNAL in the VSI OpenVMS RTL Library (LIB$) Manual.

On Alpha systems, a standard function returns its function value in R0, F0, or F0 and F1. A function value of less than 64 bits returned by immediate value in R0 is zero-extended or sign-extended to a full quadword as required by the data type. Note that a floating function value is returned by immediate value in F0 or in F0 and F1.

For I64, values up to 128 bits are returned directly in the registers (R8, R9 or F8, F9), according to the rules in Table 2.31. Integer, enumeration, record, and set values (bit vectors) smaller than 64 bits must be zero-filled (unsigned integers, enumerations, records, sets) or sign-extended (signed integrals) to a full 64 bits. However, for unsigned 32-bit integers, bit 31 is replicated in bits 32-63.

When floating-point values are returned in floating-point registers, they are returned in the register format, rounded to the appropriate precision. When they are returned in the general registers (for example, as part of a record), they are returned in their memory format.

3.2.3.1. Facility Return Status and Condition Value Symbols

Library return status and condition value symbols have the following general form:
fac$_abcmnoxyz
The elements that make up this format represent the following:

fac

The 2- or 3-letter facility symbol

abc

The first 3 letters of the first word of the associated message

mno

The first 3 letters of the next word

xyz

The first 3 letters of the third word, if any

Articles and prepositions are not considered significant words in this format. If a significant word is only two letters long, an underscore is used to fill out the third space. Some examples follow. Note that, In most facilities, the normal or success symbol is an exception to the convention described here.

SS$_NORMAL

Routine successfully completed

LIB$_INSVIRMEM

Insufficient virtual memory

MTH$_FLOOVEMAT

Floating overflow in mathematics library procedure

OTS$_FATINTERR

Fatal internal error in a language-independent support procedure

LIB$_SCRBUFOVF

Screen buffer overflow

3.3. Calling a Library Procedure in VAX MACRO (VAX Only)

This section describes how to code MACRO calls to library routines using a CALLS, CALLG, or JSB instruction for VAX systems. The routine descriptions that appear later in this manual describe the entry points for each routine. You can use either a CALLS or a CALLG instruction to invoke a procedure with a CALL entry point. You must use a JSB instruction to invoke a procedure with a JSB entry point. All MACRO calls are explicitly defined.

3.3.1. VAX MACRO Calling Sequence

All run-time library routines have a CALL entry point. Some routines also have a JSB entry point. In MACRO, you invoke a CALL entry point with a CALLS or CALLG instruction. To access a JSB entry point, use a JSB instruction.

Arguments are passed to CALLS and CALLG entry points by a pointer to the argument list. The only difference between the CALLS and CALLG instructions is as follows:
  • For CALLS, the calling procedure pushes the argument list onto the stack (in reverse order) before performing the call. The list is automatically removed from the stack upon return.

  • For CALLG, the calling program specifies the address of the argument list, which can be anywhere in memory. This list remains in memory upon return.

Both of these instructions have the same effect on the called procedure.

JSB instructions execute faster than CALL instructions. They do not set up a new stack frame, do not change the enabling of hardware traps or faults, and do not preserve the contents of any registers before modifying them. For these reasons, you must be careful when invoking a JSB entry point in order to prevent the loss of information stored by the calling program.

Whichever type of call you use, the actual reference to the procedure entry point should use general-mode addressing (G^). This ensures that the linker and the image activator are able to locate the module within the shareable image.

In most cases, you have to tell a library routine where to find input values and store output values. You must select a data type for each argument when you code your program. Most routines accept and return 32-bit arguments.

For input arguments of byte, word, or longword values, you can supply a constant value, a variable name, or an expression in the run-time library routine call. If you supply a variable name for the argument, the data type of the variable must be as large as or larger than the data types that the called procedure requires. For example, if the called procedure expects a byte in the range 0 to 100, you can use a variable data type of a byte, word, or longword with a value between 0 and 100.

For each output argument, you must declare a variable of exactly the length required to avoid extraneous data. For example, if the called procedure returns a byte value to a word-length variable, the leftmost 8 bits of the variable <15:8> are not overwritten on output. Conversely, if a procedure returns a longword value to a word-length variable, it modifies variables in the next higher word.

3.3.2. VAX MACRO CALLS Instruction Example

Before executing a CALLS instruction, you must push the necessary arguments on the stack. Arguments are pushed in reverse order; the last argument listed in the calling sequence is pushed first. The following example shows how a MACRO program calls the procedure that allocates virtual memory in the program region for LIB$GET_VM.
        .PSECT DATA     PIC,USR,CON,REL,GBL,NOSHR,NOEXE,RD,WRT,NOVEC

MEM:    .LONG   0                       ; Longword to hold address of
                                        ; allocated memory
LEN:    .LONG   700                     ; Number of bytes to allocate

        .PSECT  CODE    PIC,USR,CON,REL,GBL,SHR,EXE,RD,NOWRT,NOVEC

        .ENTRY   PROG, ^M<>

        PUSHAL   MEM                    ; Push address of longword
                                        ; to receive address of block
        PUSHAL   LEN                    ; Push address of longword
                                        ; containing number of bytes
                                        ; desired
        CALLS    #2, G^LIB$GET_VM       ; Allocate memory
        BLBC     R0, 1$                 ; Branch if memory not available
        RET
1$:     PUSHL    R0                     ; Signal the error
        CALLS    #1, G^LIB$SIGNAL
        RET

        .END     PROG

Because the stack grows toward location 0, arguments are pushed onto the stack in reverse order from the order shown in the general format for the routine. Thus, the base-address argument, here called START, is pushed first, and then the number-bytes argument, called LEN. Upon return from LIB$GET_VM, the calling program tests the return status (ret-status), which is returned in R0 and branches to an appropriate error routine if an error occurred.

3.3.3. VAX MACRO CALLG Instruction Example

When you use the CALLG instruction, the arguments are set up in any location, and the call includes a reference to the argument list. The following example of a CALLG instruction is equivalent to the preceding CALLS example.
ARGLST:
        .LONG         2               ; Argument list count
        .ADDRESS      LEN             ; Address of longword containing
                                      ; the number of bytes to allocate.
        .ADDRESS      START           ; Address of longword to receive
                                      ; the starting address of the
                                      ; virtual memory allocated.

LEN:    .LONG         20              ; Number of bytes to allocate
START:  .BLKL         1               ; Starting address of the virtual
                                      ; memory.


        CALLG  ARGLIST, G^LIB$GET_VM  ; Get virtual memory
        BLBC   R0, ERROR              ; Check for error
        BRB    10$

3.3.4. VAX MACRO JSB Entry Points

A procedure's JSB entry point name indicates the highest numbered register that the procedure modifies. Thus, a procedure with a suffix Rn modifies registers R0 through Rn. (You should always assume that R0 and R1 are modified). The calling program loads the arguments in the registers before the JSB instruction is executed.

A calling program must use a JSB instruction to invoke a run-time library routine by means of its JSB entry point. You pass arguments to a JSB entry point by placing them in registers in the following manner:
NUM:    .FLOAT      0.7853981           ; Constant P1/4
        MOVF        NUM, R0             ; Set up input argument
        JSB         G^MTH$SIN_R4        ; Call F_floating sine procedure
                                        ; Return with value in R0

In this example, R4 in the entry point name indicates that MTH$SIN_R4 changes the contents of registers R0 through R4. The routine does not reference or change the contents of registers R5 through R11.

The entry mask of a calling procedure should specify all the registers to be saved if the procedure invokes a JSB routine. This step is necessary because a JSB procedure does not have an entry mask and thus has no way to specify registers to be saved or restored.

For example, consider program A calling procedure B by means of a CALL entry point.
  • Procedure B modifies the contents of R2 through R6, so the contents of these registers are preserved at the time of the call.

  • Procedure B then invokes procedure C by means of a JSB entry point.

  • Procedure C modifies registers R0 through R7.

  • When control returns to procedure B, R7 has been modified, but when procedure B passes control back to procedure A, it restores only R2 through R6. Thus, the contents of R7 are unpredictable, and program A does not execute as expected. Procedure B should be rewritten so that R2 through R7 are saved in procedure B's entry mask.

A similar problem occurs if the stack is unwound, because unwinding the stack restores the contents of registers for each stack frame as it removes the previous frame. Because a JSB entry point does not create a stack frame, the contents of the registers before the JSB instruction will not be restored unless they were saved in the entry mask of the calling program. You do this by naming the registers to be saved in the calling program's entry mask, so a stack unwind correctly restores all registers from the stack. In the following example, the function Y=PROC(A,B) returns the value Y, where Y = SIN(A)*SIN(B):
     .ENTRY  PROC, ^M <R2, R3, R4, R5>    ; Save R2:R5
     MOVF    @4(AP), R0                   ; R0 = A
     JSB     G^MTH$SIN_R4                 ; R0 = SIN(A)
     MOVF    R0 , R5                      ; Copy result to register
                                          ; not modified by MTH$SIN
     MOVF    @8(AP) , R0                  ; R0 = B
     JSB     G^MTH$SIN_R4                 ; R0 = SIN(B)
     MULF    R5 , R0                      ; R0 = SIN(A)SIN(B)
     RET                                  ; Return

3.3.5. Return Status

Your VAX MACRO program can test for errors by examining segments of the 32-bit status code returned by a run-time library routine.

To test for errors, check for a zero in bit 0 using a Branch on Low Bit Set (BLBS) or Branch on Low Bit Clear (BLBC) instruction.

To test for a particular condition value, compare the 32 bits of the return status with the appropriate return status symbol using a Compare Long (CMPL) instruction or the run-time library routine LIB$MATCH_COND.

There are three ways to define a symbol for the condition value returned by a run-time library routine so that you can compare the value in R0 with a particular error code:
  • Using the .EXTRN symbol directive. This causes the assembler to generate an external symbol declaration.

  • Using the $facDEF macro call. Calling the $LIBDEF macro, for example, causes the assembler to define all LIB$ condition values.

  • By default. The assembler automatically declares the condition value as an external symbol that is defined as a global symbol in the run-time library.

The following example asks for the user's name. It then calls the run-time library routine LIB$GET_INPUT to read the user's response from the terminal. If the string returned is longer than 30 characters (the space allocated to receive the name), LIB$GET_INPUT returns in R0 the condition value equivalent to the error LIB$_INPSTRTRU, 'input string truncated.' This value is defined as a global symbol by default. The example then checks for the specific error by comparing LIB$_INPSTRTRU with the contents of R0. If LIB$_INPSTRTRU is the error returned, the program considers that the routine executed successfully. If any other error occurs, the program handles it as a true error.
        $SSDEF                                  ; Define SS$ symbols
        $DSCDEF                                 ; Define DSC$ symbols
        .PSECT   $DATA
PROMPT_D:                                       ; Descriptor for prompt
        .WORD    PROMPT_LEN                     ; Length field
        .BYTE    DSC$K_DTYPE_T                  ; Type field is text
        .BYTE    DSC$K_CLASS_S                  ; Class field is string
        .ADDRESS PROMPT                         ; Address

PROMPT: .ASCII   /NAME: /                       ; String descriptor
PROMPT_LEN = . - PROMPT                         ; Calculate length of
                                                ; string

STR_LEN = 30                                    ; Use 30-byte string
STRING_D:                                       ; Input string descriptor
        .WORD    STR_LEN                        ; Length field
        .BYTE    DSC$K_DTYPE_T                  ; Type field in text
        .BYTE    DSC$K_CLASS_S                  ; Class field is string
        .ADDRESS STR_AREA                       ; Address
STR_AREA: .BLKB   STR_LEN                       ; Area to receive string

                 .PSECT $CODE
                 .ENTRY START , ^M<>
                 PUSHAQ PROMPT_D                ; Push address of prompt
                                                ; descriptor
                 PUSHAQ STRING_D                ; Push address of string
                                                ; descriptor

                 CALLS  #2 , G^LIB$GET_INPUT    ; Get input string
                 BLBS   R0 , 10$                ; Check for success
                 CMPL   R0 , #LIB$_INPSTRTRU    ; Error: Was it
                                                ; truncated string?
                 BEQL   10$                     ; No, more serious error
                 PUSHL  R0
                 CALLS  #1 , G^LIB$SIGNAL

10$:             MOVL   #SS$_NORMAL , R0        ; Success, or name too
                                                ; long
                 RET
                .END    START

3.3.6. Function Return Values in VAX MACRO (VAX and Alpha)

Function values are generally returned in R0 (32-bit values) or R0 and R1 (64-bit values). A MACRO program can access a function value by referencing R0 or R0 and R1 directly. For functions that return a string, the address of the string or the address of its descriptor is returned in R0. If a function needs to return a value larger than 64 bits, it must return the value by using an output argument.

Note the following exceptions to these rules:
  • JSB entry points in the MTH$ facility return H_floating values in R0 through R3.

  • One routine, MTH$SINCOS, returns two function values: the sine and the cosine of an angle. Depending on the data type of the function values, the function values are returned in the following registers:

    F_floating

    R0 and R1

    D_floating

    R0 through R3

    G_floating

    R0 through R3

    H_floating

    R0 through R7

As in the case of output arguments, a variable declared to receive the function values must be the same length as the value.

3.4. Calling a Library Routine in BLISS

This section describes how to code BLISS calls to library routines. A called routine can return only one of the following:
  • No value.

  • A function value (typically, an integer or floating point number). For example, MTH$SIN returns its result as an F_floating value in R0 on VAX systems, in F0 on Alpha systems, or in R8 on I64 systems.

    On Alpha processors, BLISS cannot access floating point registers. Direct use of the I64 floating-point registers is not supported.

  • A return status (typically, a 32-bit condition value) indicating that the routine has either executed successfully or failed. For example, LIB$GET_INPUT returns a return status in R0 (R8, R9 for I64). If the routine executes successfully, it returns SS$_NORMAL; if not, it returns one of several possible error condition values. BLISS treats the return status like any other value.

3.4.1. BLISS Calling Sequence

Scalar arguments are usually passed to run-time library routines by reference. Thus, when a BLISS program passes a variable, the variable appears with no preceding period in the procedure-call actual argument list. A constant value can be easily passed by using the %REF built-in function.

The following example shows how a BLISS program calls LIB$PUT_OUTPUT. This routine writes a record at the user's terminal.
MODULE SHOWTIME(IDENT='1-1' %TITLE'Print time', MAIN=TIMEOUT)=
BEGIN
LIBRARY 'SYS$LIBRARY:STARLET';  ! Defines system services, etc.

MACRO
    DESC[]=%CHARCOUNT(%REMAINING),  ! VAX string descriptor
        UPLIT BYTE(%REMAINING) %;   !   definition
BIND
        FMTDESC=UPLIT( DESC('At the tone, the time will be ',
                %CHAR(7), '!%T' ));
EXTERNAL ROUTINE
        LIB$PUT_OUTPUT: ADDRESSING_MODE(GENERAL);

ROUTINE TIMEOUT
        =
        BEGIN
        LOCAL
            TIMEBUF: VECTOR[2],        ! 64-bit system time
            MSGBUF: VECTOR[80,BYTE],   ! Output message buffer
            MSGDESC: BLOCK[8,BYTE],    ! Descriptor for message buffer
            RSLT: WORD;                ! Length of result string


!+
! Initialize the fields of the string descriptor.
!-
        MSGDESC[DSC$B_CLASS]=DSC$K_CLASS_S;
        MSGDESC[DSC$B_DTYPE]=DSC$K_DTYPE_T;
        MSGDESC[DSC$W_LENGTH]=80;
        MSGDESC[DSC$A_POINTER]=MSGBUF[0]

        $GETTIM(TIMADR=TIMEBUF);       ! Get time as 64-bit integer

        $FAOL(CTRSTR=FMTDESC,          ! Format descriptor
            OUTLEN=RSLT,               ! Output length (only a word!)
            OUTBUF=MSGDESC,                     ! Output buffer desc.
            PRMLST= %REF(TIMEBUF));             ! Address of 64-bit
                                                !  time block
        MSGDESC [DSC$W_LENGTH] = .RSLT;         ! Modify output desc.
        RETURN (LIB$PUT_OUTPUT(MSGDESC);        ! Return status
        END;
END
ELUDOM

3.4.2. Accessing a Return Status in BLISS

BLISS accesses a function return value or condition value returned in R0 (R8, R9 for I64) as follows:
STATUS = LIB$PUT_OUTPUT(MSG_DESC);
IF NOT .STATUS THEN LIB$STOP(.STATUS);

3.4.3. Calling JSB Entry Points from BLISS

Note

I64 register usage differs from that of Alpha and VAX. If you use OpenVMS high-level languages, the register and register mapping differences in the calling standards are handled by the compilers and are not exposed to your code. However, if your code uses BLISS linkages to interface with Macro-32 source code, your code might have to take into account the differences in register mapping.

BLISS added a new qualifier and source level switch to enable register mapping for register numbers in linkage and register declarations. It is off by default. BLISS also has additional support for linkages that reference arguments.

See your compiler documentation for additional information.

Many of the library mathematics routines have JSB entry points. You can invoke these routines efficiently from a BLISS procedure using LINKAGE and EXTERNAL ROUTINE declarations, as in the following example:
MODULE JSB_LINK (MAIN = MATH_JSB,     ! Example of using JSB linkage
        IDENT = '1-001',
        ADDRESSING_MODE(EXTERNAL = GENERAL)) =
BEGIN
LINKAGE
        LINK_MATH_R4 = JSB (REGISTER = 0;  ! input reg
                            REGISTER = 0): ! output reg
                       NOPRESERVE (0,1,2,3,4)
                       NOTUSED (5,6,7,8,9,10,11);
EXTERNAL ROUTINE
        MTH$SIND_R4 : LINK_MATH_R4;

FORWARD ROUTINE
        MATH_JSB;
LIBRARY 'SYS$LIBRARY:STARLET.L32';

ROUTINE MATH_JSB =                  ! Routine

    BEGIN
    LOCAL
        INPUT_VALUE : INITIAL (%E'30.0'),
        SIN_VALUE;

!+
! Get the sine of single floating 30 degrees.  The input, 30 degrees,
! is passed in R0, and the answer, is returned in R0.  Registers
! 0 to 4 are modified by MTH$SIND_R4.
!-

    MTH$SIND_R4 (.INPUT_VALUE ; SIN_VALUE);

    RETURN SS$_NORMAL;
    END;                            ! End of routine

END                     ! End of module JSB_LINK
ELUDOM

Chapter 4. Calling System Services

The OpenVMS operating system kernel has many services that are made available to application and system programs for use at run time. These system services are procedures that the OpenVMS operating system uses to control resources available to processes; to provide for communication among processes; and to perform basic operating system functions, such as the coordination of input/output operations.

This chapter describes the basic methods and conventions for coding calls to system services from OpenVMS high-level languages or from an assembly language.

For more information about using the system services that support 64-bit addressing and to see example programs that demonstrate the use of these services, refer to VSI OpenVMS Programming Concepts Manual, Volume I.

4.1. Overview

System services are called by using the conventions of the VSI OpenVMS Calling Standard. The programming languages that generate VAX, Alpha, or I64 native mode instructions provide mechanisms for specifying the procedure calls.

When you call a system service from your program, you must furnish whatever arguments the routine requires. When the system service procedure completes execution, in most cases it returns control to your program. If the service returns a status code, your program should check the value of the code to determine whether or not the service completed successfully. If the return status indicates an error, you may want to change the flow of execution of your program to handle the error before returning control to your program.

When you write a program that calls a system service in the OpenVMS operating system, the operating system views your program as a user procedure. User procedures also can call other user procedures that are either supplied by VSI or written by you. Because an OpenVMS native-mode language compiler program exists outside the operating system, compiler generated programs calling any system service are also defined as a set of user procedures.

If you program in a high-level language, refer to Chapter 5 for information about the SYS$LIBRARY:SYS$LIB_C.TLB file, which is an OpenVMS Alpha and OpenVMS I64 library of C header files.

For VAX MACRO, system service macros generate argument lists and CALL instructions to call system services. These macros are located in the system library (see SYS$LIBRARY:STARLET.MLB). When you assemble a source program, this library is searched automatically for unresolved references. (See Appendix A for further details.) Similar macros are available for BLISS and are located in SYS$LIBRARY:STARLET.REQ.

4.2. Preserving System Integrity

As described in this document and the VSI OpenVMS System Services Reference Manual, many system services are available and suitable for application programs, but the use of some of these powerful services must be restricted to protect the performance of the system and the integrity of user processes.

For example, because the creation of permanent mailboxes uses system dynamic memory, the unrestricted use of permanent mailboxes could decrease the amount of memory available to other users. Therefore, the ability to create permanent mailboxes is controlled: a user must be specifically assigned the privilege to use the Create Mailbox (SYS$CREMBX) system service to create a permanent mailbox.

The various controls and restrictions applied to system service usage are described in this chapter. The Description section of each system service in the VSI OpenVMS System Services Reference Manual lists any privileges and quotas necessary to use the service.

4.2.1. User Privileges

The system manager, who maintains the user authorization file for the system, grants privileges for access to the protected system services. The user authorization file contains, in addition to profile information about each user, a list of specific user privileges and resource quotas.

When you log in to the system, the privileges and quotas assigned to you are associated with the process created on your behalf. These privileges and quotas are applied to every image the process executes.

When an image issues a call to a system service that is protected by privilege, the privilege list is checked. If you have the specific privilege required, the image is allowed to execute the system service; otherwise, a condition value indicating an error is returned.

For a list of privileges, see the description of the Create Process ($CREPRC) system service in the VSI OpenVMS System Services Reference Manual.

4.2.2. Resource Quotas

Many system services require certain system resources for execution. These resources include system dynamic memory and process quotas for I/O operations. When a system service that uses a resource controlled by a quota is called, the process's quota for that resource is checked. If the process has exceeded its quota, or if it has no quota allotment, an error condition value may be returned.

4.2.3. Access Modes

A process can execute at any one of four access modes: user, supervisor, executive, or kernel. The access modes determine a process's ability to access pages of virtual memory. Each page has a protection code associated with it, specifying the type of access—read, write, or no access—allowed for each mode.

For the most part, user-written programs execute in user mode; system programs executing at the user's request (system services, for example) may execute atone of the other three, more privileged access modes.

In some system service calls, the access mode of the caller is checked. For example, when a process tries to cancel timer requests, it can cancel only those requests that were issued from the same or less privileged access modes. For example, a process executing in user mode cannot cancel a timer request made from supervisor, executive, or kernel mode.

Note that many system services use access modes to protect system resources, and thus employ a special convention for interpreting access mode arguments. You can specify an access mode using a numeric value or a symbolic name. Table 4.1 shows the access modes and their numeric values, symbolic names, and privilege ranks.
Table 4.1. OpenVMS System Access Modes

Access Mode

Numeric Value

Symbolic Name

Privilege Rank

Kernel

0

PSL$C_KERNEL

Highest

Executive

1

PSL$C_EXEC

Supervisor

2

PSL$C_SUPER

User

3

PSL$C_USER

Lowest

The symbolic names are defined by the symbolic definition macro SYS$PSLDEF.

System services that permit an access mode argument allow callers to specify only an access mode of equal or lesser privilege than the access mode from which the service was called. If the specified access mode is more privileged than the access mode from which the service was called, the less privileged access mode is always used.

To determine the mode to use, the operating system compares the specified access mode with the access mode from which the service was called. Because this operation results in an access mode with a higher numeric value (when the access mode of the caller is different from the specified access mode), the access mode is said to be maximized.

Because much of the code you write executes in user mode, you can omit the access mode argument. The argument value defaults to 0 (kernel mode), and when this value is compared with the value of the current execution mode (3, user mode), the higher value (3) is used.

4.3. System Service Call Entry

The Format section of each system service description in the VSI OpenVMS System Services Reference Manual indicates the positional dependencies and keyword names of each argument, as shown in the following format:
$SERVICE arga ,argb ,argc ,argd

This format indicates that the macro name of the service is $SERVICE and that it requires four arguments, ordered as shown and with keyword names arga, argb, argc, and argd.

Arguments passed to a service must be listed in your call entry in the order shown in the Format section of the service description. Each argument has four characteristics: OpenVMS usage, data type, access type, and passing mechanism. These characteristics are described in Chapter 1.

The OpenVMS Alpha and OpenVMS I64, SYS$LIBRARY:SYS$LIB_C.TLB file contains C function prototypes for system services. These prototypes are documented in VSI OpenVMS System Services Reference Manual: A-GETUAI and VSI OpenVMS System Services Reference Manual: GETUTC-Z. For each prototype, the manuals provide the correct syntax (which shows the arguments the function accepts in the order in which it expects them), a description of each argument, and the type of data returned by the function.

Some arguments are optional. Optional arguments are indicated by brackets in the service descriptions. When your program invokes a system service by using a CALL entry point, you can omit optional arguments at the end of the argument list. If the optional argument is not the last argument in the list, you must either pass a zero by value or use a comma to indicate the place of the omitted argument. Some languages, such as C, require that you pass a zero by value for all trailing optional arguments. See your language processor documentation for further information.

In the call statement of a high-level language program, you must prefix the macro function service name with SYS (the system service facility prefix). For example, the call statement in a C program procedure that calls the SYS$GETDVI system service with four arguments is as follows:
return_status = sys$getdvi
                 (event_flagnum, channel, &devnam, &item_list,0,0,0);

Note that in C, you must not omit the optional trailing arguments and should pass a zero by value for these unused parameters. See your language processor documentation for further information.

The VSI OpenVMS System Services Reference Manual provides a description of each service that indicates how each argument is to be passed. Phrases such as an address and address of a character string descriptor identify reference and descriptor arguments, respectively. Terms like Boolean value, number, value, or mask indicate an argument that is passed by value.

In the Alpha, VAX, and I64 environments, the called routine interprets each argument using one of three standard passing mechanisms: by value, by reference, or by descriptor.

On VAX systems, the calling program passes an argument list of longwords to a called service; each longword in the argument list specifies a single argument.

On Alpha systems, the calling program passes arguments in an argument item sequence; each quadword in the sequence specifies a single argument item. Note that the argument item sequence is formed using R16—R21 or F16—F21 (a register for each argument).

On I64 systems, the first eight parameters are passed in R32 through R39, with the parameter count in R25 and subsequent parameters in quadwords on the stack.

For more detailed information on arguments lists and passing mechanisms, see Section 2.4 and Section 2.5.

Some services also require service-specific data structures that either indicate functions to be performed or hold information to be returned. The VSI OpenVMS System Services Reference Manual includes descriptions of these service-specific data structures. You can use this information and information from your programming language manuals to define such service-specific item lists.

4.4. System Service Completion

When a system service completes, control is returned to your program. You can specify how and when control is returned to your program by choosing synchronous or asynchronous forms of system services and by enabling process execution modes.

The following sections describe:
  • When synchronous system services return control to your program

  • When asynchronous system services return control to your program

  • How you can synchronize the completion of asynchronous system services

  • How control is returned to your program when special process execution modes are enabled

4.4.1. Asynchronous and Synchronous System Services

You can execute a number of system services either asynchronously or synchronously (such as, SYS$GETJPI and SYS$GETJPIW or SYS$ENQ and SYS$ENQW). The W at the end of the system service name indicates the synchronous version of the system service.

The asynchronous version of a system service queues a request and returns control to your program. You can perform operations while the system service executes; however, you should not attempt to access information returned by the service until you check for the system service completion.

Typically, you pass to an asynchronous system service an event flag and an I/O status block or a lock status block. When the system service completes, it sets the event flag and places the final status of the request in the status block. You use the SYS$SYNCH system service to ensure that the system service has completed. You pass SYS$SYNCH the event flag and the status block that you passed to the asynchronous system service; SYS$SYNCH waits for the event flag to be set, then ensures that the system service (rather than some other program) sets the event flag by checking the status block. If the status block is still zero, SYS$SYNCH waits until the status block is filled.

The synchronous version of a system service acts exactly as if you had used the asynchronous version followed immediately by a call to SYS$SYNCH. If you omit the efn argument, the service uses event flag number 0 whether you use the synchronous or asynchronous version of a system service.

Example 4.1 illustrates the use of the SYS$SYNCH system service to check the completion status of the asynchronous service SYS$GETJPI.
Example 4.1. Example of SYS$SYNCH System Service in FORTRAN
! Data structure for SYS$GETJPI
.
.
.
INTEGER*4 STATUS,
2         FLAG,
2         PID_VALUE
! I/O status block
INTEGER*2 JPISTATUS,
2         LEN
INTEGER*4 ZERO /0/
COMMON /IO_BLOCK/ JPISTATUS,
2                 LEN,
2                 ZERO
.
.
.

! Call SYS$GETJPI and wait for information
STATUS = LIB$GET_EF (FLAG)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))

STATUS = SYS$GETJPI (%VAL(FLAG),
2                    PID_VALUE,
2                    ,
2                    NAME_BUF_LEN,
2                    JPISTATUS,
2                    ,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
.
.
.
STATUS = SYS$SYNCH (%VAL(FLAG),
2                   JPISTATUS)
IF (.NOT. JPISTATUS) THEN
  CALL LIB$SIGNAL (%VAL(JPISTATUS))
END IF

END

4.4.2. System Service Resource Wait Mode

Normally, when a system service is called and a required resource is not available, the process is placed in a wait state until the resource becomes available. Then the service completes execution. This mode is called resource wait mode.

In a real-time environment, however, it may not be practical or desirable for a program to wait. In these cases, you can choose to disable resource wait mode so that when a required resource is unavailable, control returns immediately to the calling program with an error condition value. You can disable (and reenable) resource wait mode with the Set Resource Wait Mode (SYS$SETRWM) system service.

If resource wait mode is disabled, it remains disabled until it is explicitly reenabled or until your process is deleted. For example, if your program has disabled resource wait mode and has exited to the DCL prompt, subsequent programs or utilities invoked by this process continue to run with resource wait mode disabled and might not perform properly because they are not prepared to handle a failure to obtain a resource. In this case, you should reenable the wait mode before your program exits to the DCL prompt.

How a program responds to the unavailability of a resource depends primarily on the application and the particular service being called. In some instances, the program may be able to continue execution and retry the service call later. In other instances, it may be necessary for the program to wait for the resource and the system service to complete.

4.4.3. Condition Values Returned from System Services

When a service returns control to your program, it places a return status value in the general register R0 (R8, R9 for I64). The value in the low-order word indicates either that the service completed successfully or that some specific error prevented the service from performing some or all of its functions. After each call to a system service, you must check whether it completed successfully. You can also test for specific errors in the condition value.

Depending on your specific needs, you can test just the low-order bit, the low-order 3 bits, or the entire condition value, as follows:
  • The low-order bit indicates successful (1) or unsuccessful (0) completion of the service.

  • The low-order 3 bits, taken together, represent the severity of the error. Table 4.2 lists the possible severity code values returned.

    For VAX MACRO, the symbolic definition macro SYS$STSDEF defines the symbolic names. For the C programming language, the SSDEF.H file defines the symbolic names.

  • The remaining bits (bits 3 through 31) classify the particular return condition and the operating system component that issued the condition value. For system service return status values, the high-order word (bits 16 through 31) contains zeros.


Table 4.2. Severity Codes of Condition Value Returned

Value

Meaning

Symbolic Name

0

Warning

STS$K_WARNING

1

Success

STS$K_SUCCESS

2

Error

STS$K_ERROR

3

Informational

STS$K_INFO

4

Severe or fatal error

STS$K_SEVERR

5—7

Reserved

Each numeric condition value has a unique symbolic name in the following format:
SS$_code

where code is a mnemonic describing the return condition.

For example, the following symbol usually indicates a successful return:
SS$_NORMAL
An example of an error return condition value is as follows:
SS$_ACCVIO

This condition value indicates that an access violation occurred because a service could not read an input field or write an output field.

The symbolic definitions for condition values are included in the default system library SYS$LIBRARY:STARLET.OLB. You can obtain a listing of these symbolic codes at assembly time by invoking the system macro SYS$SSDEF. To check return conditions, use the symbolic names for system condition values.

The OpenVMS operating system does not automatically handle system service failure or warning conditions; you must test for them and handle them yourself. This contrasts with the operating system's handling of exception conditions detected by the hardware or software; the system handles these exceptions by default, although you can intervene in or override the default handling by declaring a condition handler.

4.4.4. Testing the Condition Value

Each language provides some mechanism for testing the return status. Often you need only check the low-order bit, such as by a test for TRUE (success or informational return) or FALSE (error or warning return). Condition values that are returned by system services can provide information and whether the service completed successfully. The condition value that usually indicates success is SS$_NORMAL, but others are defined. For example, the condition value SS$_BUFFEROVF, which is returned when a character string returned by a service is longer than the buffer provided to receive it, is a success code. This condition value, however, gives the program additional information.

Warning returns and some error returns indicate that the service performed some, but not all, of the requested function.

The possible condition values that each service can return are described with the individual service descriptions in the VSI OpenVMS System Services Reference Manual. When you write calls to system services, read the descriptions of the return condition values to determine whether you want the program to check for particular return conditions.

To check the entire value for a specific return condition, each language provides a way for your program to determine the values associated with specific symbolically defined codes. You should always use these symbolic names when you write tests for specific conditions.

For information about how to test for these codes, see the user's guide for your programming language.

4.4.4.1. Testing the Condition Value With $VMS_STATUS_SUCCESS Macro

You can use the $VMS_STATUS_SUCCESS macro, defined in stsdef.h, to test an OpenVMS condition value. $VMS_STATUS_SUCCESS depends on the documented format of an OpenVMS condition value, and particularly on the setting of the lowest bit in a condition value. If the lowest bit is set, the condition indicates a successful status, while the bit is clear for an unsuccessful status.

$VMS_STATUS_SUCCESS is used only with condition values that follow the OpenVMS condition status value format, and not with C standard library routines and return values that follow C native status value norms. For details on the OpenVMS condition status value structure, please see VSI OpenVMS Programming Concepts Manual, Volume I. For information on the return values from the various C standard library routines, see the VSI C Run-Time Library Reference Manual for OpenVMS Systems.

For example, the following code demonstrates a test that causes are turn on error.
RetStat = sys$dassgn( IOChan );
if (!$VMS_STATUS_SUCCESS( RetStat ))
  return RetStat;

4.4.5. Special Condition Values Using Symbolic Codes

Individual services have symbolic codes for special return conditions, argument list offsets, identifiers, and flags associated with these services. For example, the Create Process (SYS$CREPRC) system service(which is used to create a subprocess or a detached process) has symbolic codes associated with the various privileges and quotas you can grant to the created process.

The SYS$LIBRARY:SYS$LIB_C.TLB file contains the C header files for OpenVMS Alpha and OpenVMS I64 C data structures and definitions. For more information about SYS$LIBRARY:SYS$LIB_C.TLB, refer to Chapter 5.

The default system macro library, STARLET.MLB, contains the macro definitions for most system symbols. When you assemble a source program that calls any of these macros, the assembler automatically searches STARLET.MLB for the macro definitions. Each symbol name has a numeric value.

If your language has a method of obtaining values for these symbols, this method is explained in the user's guide.

If your language does not have such a method, you can do the following:
  1. Write a short VAX MACRO program containing the desired macros.

  2. Assemble or compile the program and generate a listing. Using the listing, find the desired symbols and their hexadecimal values.

  3. Define each symbol with its value within your source program.

For example, to use the Get Job/Process Information ($GETJPI) system service to find out the accumulated CPU time (in 10-millisecond ticks) for a specified process, you must obtain the value associated with the item identifier JPI$_CPUTIM. You can do this in the following way:
  1. Create the following three-line VAX MACRO program (named JPIDEF.MAR here; you can choose any name you want):
    .TITLE  JPIDEF  "Obtain values for $JPIDEF"
    $JPIDEF GLOBAL          ; These MUST be UPPERCASE
    .END
  2. On VAX, assemble and link the program to create the file JPIDEF.MAP as follows:
    $ MACRO JPIDEF
    $ LINK/NOEXE/MAP/FULL  JPIDEF
    %LINK-W-USRTFR, image NL:[].EXE; has no user transfer address

    The file JPIDEF.MAP contains the symbols defined by $JPIDEF listed both alphabetically and numerically.

    On Alpha and I64, to compile the program to create the JPIDEF.MAP, enter the following:
    $ MACRO/MIGRATION JPIDEF
    $ LINK/NOEXE/MAP/FULL  JPIDEF
    %LINK-W-USRTFR, image NL:[].EXE; has no user transfer address
  3. Find the value of JPI$_CPUTIM and define the symbol in your program.

4.4.6. Testing the Return Condition Value for VAX MACRO

To check for successful completion after a system service call, the program can test the low-order bit of R0 and branch to an error-checking routine if this bit is not set, as follows:
    BLBC    R0,errlabel             ; Error if low bit clear

Programs should not test for success by comparing the return status to SS$_NORMAL. A future release of OpenVMS may add new, alternate success codes to an existing service, causing programs that test for SS$_NORMAL to fail.

The error-checking routine may check for specific values or for specific severity levels. For example, the following VAX MACRO instruction checks for an illegal event flag number error condition:
    CMPL    #SS$_ILLEFC,R0        ; Is event flag number illegal?

Note that return condition values are always longword values; however, all system services always return the same value in the high-order word of all condition values returned in R0.

4.4.7. System Messages Generated by Condition Values

When you execute a program with the DCL command RUN, the command interpreter uses the contents of R0 to issue a descriptive message if the program completes with an unsuccessful status. On I64, the calling standard specifies that the return status is returned in R8. As an aid to portable code, the MACRO compiler automatically maps uses of R0 to R8. See the VSI OpenVMS MACRO Compiler Porting and User's Guide for additional information.

The following VAX MACRO code fragment shows a simple error-checking procedure in a main program:
        $READEF_S -
                EFN=#64, -
                STATE=TEST
        BSBW    ERROR
         .
         .
         .
ERROR:  BLBC    R0,10$          ; Check register 0
        RSB                     ; Success, return
10$:    RET                     ; Exit with R0 status

After a system service call, the BSBW instruction branches to the subroutine ERROR. The subroutine checks the low-order bit in register 0 and, if the bit is clear, branches to a RET instruction that causes the program to exit with the status of R0 preserved. Otherwise, the subroutine issues an RSB instruction to return to the main program.

If the event flag cluster requested in this call to $READEF is not currently available to the process, the program exits and the command interpreter displays the following message:
%SYSTEM-F-UNASEFC, unassociated event flag cluster

The keyword UNASEFC in the message corresponds to the condition value SS$_UNASEFC.

The following three severe errors generated by the calls, not the services, can be returned from calls to system services:

Error

Meaning

SS$_ACCVIO

The argument list cannot be read by the caller (using the $name_G macro), and the service is not called.

This meaning of SS$_ACCVIO is different from its meaning for individual services. When SS$_ACCVIO is returned from individual services, the service is called, but one or more arguments to the service cannot be read or written by the caller.

SS$_INSFARG

Not enough arguments were supplied to the service.

SS$_ILLSER

An illegal system service was called.

4.5. Program Examples with System Service Calls

This section provides code examples that illustrate the use of a system service call in the following programming languages:
PL/I, Fortran 77, and ADA 83 are not supported on OpenVMS I64. If your application has code written in PL/I, VSI recommends rewriting it in another language such as C or C++.Update code written in Ada 83 to Ada 95, and code written in Fortran 77 to Fortran 90.
Example 4.2. System Service Call in Ada
ith SYSTEM, TEXT_IO, STARLET, CONDITION_HANDLING;  1
procedure ORION is
    -- Declare variables to hold equivalence name and length
    --
    EQUIV_NAME: STRING (1..255);  2
    pragma VOLATILE (EQUIV_NAME);
    NAME_LENGTH: SYSTEM.UNSIGNED_WORD;
    pragma VOLATILE (NAME_LENGTH);

    -- Declare itemlist and fill in entries.
    --
    ITEM_LIST: STARLET.ITEM_LIST_3_TYPE (1..2) :=  3
        (1 =>
            (ITEM_CODE   => STARLET.LNM_STRING,  4
             BUF_LEN     => EQUIV_NAME'LENGTH,
             BUF_ADDRESS => EQUIV_NAME'ADDRESS,
             RET_ADDRESS => NAME_LENGTH'ADDRESS),
         2 =>
            (ITEM_CODE   => 0,
             BUF_LEN     => 0,
             BUF_ADDRESS => SYSTEM.ADDRESS_ZERO,
             RET_ADDRESS => SYSTEM.ADDRESS_ZERO));

    STATUS: CONDITION_HANDLING.COND_VALUE_TYPE;  5

begin
    -- Translate the logical name
    --
    STARLET.TRNLNM (  6
     STATUS => STATUS,
     TABNAM => "LNM$FILE_DEV",
     LOGNAM => "CYGNUS",
     ITMLST => ITEM_LIST);

    -- Display name if success, else signal error
    --
    if not CONDITION_HANDLING.SUCCESS (STATUS) then  7
     CONDITION_HANDLING.SIGNAL (STATUS);
    else
        TEXT_IO.PUT ("CYGNUS translates to """);
        TEXT_IO.PUT (EQUIV_NAME (1..INTEGER(NAME_LENGTH)));
        TEXT_IO.PUT_LINE ("""");
    end if;
end ORION;

Ada Notes

1

(The with clause names the predefined packages of declarations used in this program. SYSTEM and TEXT_IO are standard Ada packages; STARLET defines the OpenVMS system service routines, data types, and constants; and CONDITION_HANDLING defines error-handling facilities.

2

Enough space is allocated to EQUIV_NAME to hold the longest possible logical name. NAME_LENGTH will receive the actual length of the translated logical name. The VOLATILE pragma is required for variables that will be modified by means other than an assignment statement or being an output parameter to a routine call.

3

ITEM_LIST_3_TYPE is a predeclared type in package STARLET that defines the OpenVMS three-longword item list structure.

4

The dollar-sign character is not valid in Ada identifiers; package STARLET defines the fac$ names by removing the dollar sign.

5

COND_VALUE_TYPE is a predeclared type in package CONDITION_HANDLING that is used for return status values.

6

System services are defined in package STARLET using names that omit the prefix SYS$. The passing mechanisms are specified in the routine declaration in STARLET, so they need not be specified here.

7

In this example, any failure status from the SYS$TRNLNM service is signaled as an error. Other means of error recovery are possible; see your Ada language documentation for more details.

Example 4.3. System Service Call in BASIC
10  SUB ORION  1                       ! Subprogram ORION

    OPTION TYPE=EXPLICIT                ! Require declaration of all
                                        ! symbols

    EXTERNAL LONG FUNCTION SYS$TRNLNM   ! Declare the system service
    EXTERNAL WORD CONSTANT LNM$_STRING  ! The request code that
                                        ! we will use
    DECLARE WORD NAMLEN,  2             ! Word to receive length
            LONG SYS_STATUS             ! Longword to receive status
    COMMON (BUF) STRING NAME_STRING = 255  3

    RECORD ITEM_LIST                    ! Define item
                                        ! descriptor structure
        WORD BUFFER_LENGTH              ! The buffer length
        WORD ITEM                       ! The request code
        LONG BUFFER_ADDRESS             ! The buffer address
        LONG RETURN_LENGTH_ADDRESS      ! The address of the return len
                                        ! word
        LONG TERMINATOR                 ! The terminator
    END RECORD ITEM_LIST                ! End of structure definition

    DECLARE ITEM_LIST ITEMS             ! Declare an item list
    ITEMS::BUFFER_LENGTH = 255%         ! Initialize the item list
    ITEMS::ITEM = LNM$_STRING
    ITEMS::BUFFER_ADDRESS = LOC( NAME_STRING )
    ITEMS::RETURN_LENGTH_ADDRESS = LOC( NAMLEN )
    ITEMS::TERMINATOR = 0
                                 4
    SYS_STATUS = SYS$TRNLNM( , 'LNM$FILE_DEV', 'CYGNUS',, ITEMS)  5

    IF (SYS_STATUS AND 1%) = 0%  6
    THEN
        ! Error path
    ELSE
        ! Success path
    END IF
    END SUB

BASIC Notes

1

The SUB statement defines the routine and its entry mask.

2

The DECLARE WORD NAMLEN declaration reserves a 16-bit word for the output value.

3

The COMMON (BUF) STRING NAME_STRING = 255 declaration allocates 255 bytes for the output data in a static area. The compiler builds the descriptor.

4

The SYS$ form invokes the system service as a function. Enclose the arguments in parentheses and specify them in positional order only. Specify a comma for each optional argument that you omit (including trailing arguments).

5

The input character string is specified directly in the system service call;the compiler builds the descriptor.

6

The IF statement performs a test on the low-order bit of the return status. This form is recommended for all status returns.

Example 4.4. System Service Call in BLISS
MODULE ORION=

BEGIN
EXTERNAL ROUTINE
    ERROR_PROC: NOVALUE;                 ! Error processing routine

LIBRARY 'SYS$LIBRARY:STARLET.L32';       ! Library containing OpenVMS
                                         ! macros (including $TRNLNM).
                                         ! This declaration
                                         ! is required.

GLOBAL ROUTINE ORION: NOVALUE=

    BEGIN
    OWN
        NAMBUF : VECTOR[255, BYTE],       ! Output buffer
        NAMLEN : WORD,                    ! Translated string length
        ITEMS : BLOCK[16,BYTE]
                INITIAL(WORD(255,         ! Output buffer length
                        LNM$_STRING),     ! Item code
                        NAMBUF,           ! Output buffer
                        NAMLEN,           ! Address of word for
                                          ! translated
                                          ! string length
                        0);               ! List terminator

    LOCAL                                 ! Return status from
        STATUS;                           ! system service

    STATUS = $TRNLNM(TABNAM = %ASCID'LNM$FILE_DEV',
                     LOGNAME = %ASCID'CYGNUS',
                     ITMLST = ITEMS);         1

    IF NOT .STATUS THEN ERROR_PROC(.STATUS);  2

    END;

BLISS Notes

1

The macro is invoked by its service name, without a suffix. Enclose the arguments in parentheses and specify them by keyword. (Keyword names correspond to the names of the arguments shown in lowercase in the system service format descriptions in the VSI OpenVMS System Services Reference Manual).

2

The return status, which is assigned to the variable STATUS, is tested for TRUE or FALSE. FALSE (low bit = 0) indicates failure or warning.

Example 4.5. System Service Call in C
 #include <starlet.h>  1
 #include <lib$routines.h>
 #include <ssdef.h>
 #include <lnmdef.h>
 #include <descrip.h>
 #include <stdio.h>

 typedef struct {  2
     unsigned short  buffer_length;
     unsigned short  item_code;
     char     *buffer_addr;
     short     *return_len_addr;
     unsigned     terminator;
 } item_list_t;


 main ()
 {                                          3
     $DESCRIPTOR(table_name, "LNM$FILE_DEV");
     $DESCRIPTOR(log_name, "CYGNUS");
     char    translated_name[255];
     int     status;
     short   return_length;
     item_list_t item_list;

     item_list.buffer_length = sizeof(translated_name);  4
     item_list.item_code = LNM$_STRING;
     item_list.buffer_addr = translated_name;
     item_list.return_len_addr = &return_length;
     item_list.terminator = 0;

     status = sys$trnlnm(0, &table_name, &log_name, 0, &item_list);  5

     if (!(status & 1))  6
         lib$signal(status);
     else
         printf("The logical name %s is equivalent to %*s\n",
             log_name.dsc$a_pointer,
             return_length,
             translated_name);
 }

C Notes

1

The C language header file starlet.h defines OpenVMS system services entry points. The file lib$routines.h declares the LIB$ Run-Time Library routines.

2

The structure of an item list entry is defined.

3

The $DESCRIPTOR macro declares and initializes a character string descriptor. Here, two descriptors are created for use with the sys$trnlnm system service.

4

The function sizeof is used to obtain the size of the string. The returned length will be stored as a short integer in return_length.

5

The sys$trnlnm routine is defined in starlet.h.

6

The IF statement performs a logical test following the function reference to determine whether the service completed successfully. If an error or warning occurs during the service call, the error is signaled.

Example 4.6. System Service Call in COBOL
IDENTIFICATION DIVISION.
PROGRAM-ID. ORION.  1
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TABNAM PIC X(11) VALUE "LNM$FILE_DEV".
01 CYGDES PIC X(6) VALUE "CYGNUS".
01 NAMDES PIC X(255) VALUE SPACES.  2
01 NAMLEN PIC S9(4) COMP.
01 ITMLIS.
   02 BUFLEN PIC S9(4) COMP VALUE 225.
   02 ITMCOD PIC S9(4) COMP VALUE 2.  3
   02 BUFADR POINTER VALUE REFERENCE NAMDES.
   02 RETLEN POINTER VALUE REFERENCE NAMLEN.
   02 FILLER PIC S9(5) COMP VALUE 0.
01 RESULT PIC S9(9) COMP.  4

PROCEDURE DIVISION.
START-ORION.
    CALL "SYS$TRNLNM"  5
      USING OMITTED
            BY DESCRIPTOR TABNAM
            BY DESCRIPTOR CYGDES  6
            OMITTED
            BY REFERENCE ITMLIS
      GIVING RESULT.
    IF RESULT IS FAILURE  7
       GO TO ERROR-CHECK.
    DISPLAY "NAMDES:  ", NAMDES(1:NAMLEN).
    GO TO THE-END.
ERROR-CHECK.
    DISPLAY "Returned Error: ", RESULT CONVERSION.
THE-END.
    STOP RUN.

COBOL Notes

1

The PROGRAM-ID paragraph identifies the program by specifying the program name, which is the global symbol associated with the entry point. The compiler builds the entry mask.

2

Enough bytes are allocated for the alphanumeric output data. The compiler generates a descriptor when you specify USING BY DESCRIPTOR in the CALL statement.

3

The value of the symbolic code LNM$STRING is 2. Section 4.4.5 explains how to obtain values for symbolic codes.

4

This definition reserves a signed longword with COMP (binary) usage to receive the output value.

5

The service is called by the SYS$ form of the service name, and the name is enclosed in quotation marks. Specify arguments in positional order only, with the USING statement. You cannot omit arguments; if you are accepting the default for an argument, you must pass the default value explicitly (OMITTED in this example). You can specify explicitly how each argument is being passed: by descriptor, by reference (that is, by address), or by value. You can also implicitly specify how an argument is being passed: through the default mechanism (by reference), or through association with the last specified mechanism (thus, the last two arguments in the example are implicitly passed by value).

6

The input string is defined as alphanumeric (ASCII) data. The compiler generates a descriptor when you specify USING BY DESCRIPTOR in the CALL statement.

7

The IF statement tests RESULT for a failure status. In this case, control is passed to the routine ERROR-CHECK.

Example 4.7. System Service Call in FORTRAN
 SUBROUTINE ORION
 IMPLICIT NONE                 ! Require declaration of all symbols
 INCLUDE '($SYSSRVNAM)'        ! Declare system service names  1
 INCLUDE '($LNMDEF)'           ! Declare $TRNLNM item codes
 INCLUDE '(LIB$ROUTINES)'      ! Declare LIB$ routines

 STRUCTURE /ITEM_LIST_3_TYPE/  ! Structure of item list  2
   INTEGER*2 BUFLEN            ! Item buffer length
   INTEGER*2 ITMCOD            ! Item code
   INTEGER*4 BUFADR            ! Item buffer address
   INTEGER*4 RETADR            ! Item return length address
 END STRUCTURE
 RECORD /ITEM_LIST_3_TYPE/ ITEMLIST(2)  ! Declare itemlist

 CHARACTER*255 EQUIV_NAME      ! For returned equivalence name
 INTEGER*2 NAMLEN              ! For returned name length
 VOLATILE EQUIV_NAME,NAMLEN  3

 INTEGER*4 STATUS              ! For returned service status  4

 ! Fill in itemlist
 !
 ITEMLIST(1).ITMCOD = LNM$_STRING
 ITEMLIST(1).BUFLEN = LEN(EQUIV_NAME)  5
 ITEMLIST(1).BUFADR = %LOC(EQUIV_NAME)
 ITEMLIST(1).RETADR = %LOC(NAMLEN)
 ITEMLIST(2).ITMCOD = 0               ! For terminator
 ITEMLIST(2).BUFLEN = 0

 ! Call SYS$TRNLM
 !
 STATUS = SYS$TRNLNM (,               ! ATTR omitted  6
 1                    'LNM$FILE_DEV', ! TABNAM
 2                    'CYGNUS',       ! LOGNAM
 3                    ,               ! ACMODE omitted
 4                    ITEMLIST)       ! ITMLST

 ! Check return status, display translation if successful
 !
 IF (.NOT. STATUS) THEN  7
     CALL LIB$SIGNAL(%VAL(STATUS))
 ELSE
     WRITE (*,*) 'CYGNUS translates to: "',
 1       EQUIV_NAME(1:NAMLEN), '"'
 END IF
 END

FORTRAN Notes

1

The module $SYSSRVNAM in the FORTRAN system default library FORSYSDEF.TLB contains INTEGER and EXTERNAL declarations for each of the system services, so you need not explicitly provide these declarations in your program. Module $LNMDEF defines constants and data structures used when calling the logical name services, and module LIB$ROUTINES contains declarations for the LIB$ Run-Time Library routines.

2

The structure of an OpenVMS 3-longword item list is declared and then used to define the record variable ITEM_LIST. The second element will be used for the terminator.

3

The VOLATILE declaration is required for variables that are modified by means other than a direct assignment or as an argument in a routine call.

4

Return status variables should always be declared as longword integers.

5

The LEN intrinsic function returns the allocated length of EQUIV_NAME. The %LOC built-in function returns the address of its argument.

6

By default, FORTRAN passes arguments by reference, except for strings which are passed by CLASS_S descriptor. Arguments are omitted in FORTRAN by leaving the comma as a placeholder. All arguments must be specified or explicitly omitted.

7

A condition value can be tested for success or failure by a true/false test. For more information on testing return statuses, see the OpenVMS FORTRAN documentation.

Example 4.8. System Service Call in Pascal
[INHERIT('SYS$LIBRARY:STARLET',  1
         'SYS$LIBRARY:PASCAL$LIB_ROUTINES')]
PROGRAM ORION (OUTPUT);

TYPE
    Item_List_Cell = RECORD CASE INTEGER OF  2
        1:( { Normal Cell }
            Buffer_Length : [WORD] 0..65535;
            Item_Code     : [WORD] 0..65535;
            Buffer_Addr   : UNSIGNED;
            Return_Addr   : UNSIGNED
            );
        2:( { Terminator }
            Terminator   : UNSIGNED
            );
        END;

    Item_List_Template(Count:INTEGER) = ARRAY [1..Count] OF Item_List_Cell;

VAR
    Item_List       : Item_List_Template(2);
    Translated_Name : [VOLATILE] VARYING [255] OF CHAR;  3
    Status          : INTEGER;

    BEGIN

    { Specify the buffer to return the translation }  4
    Item_List[1].Buffer_Length  := SIZE(Translated_Name.Body);
    Item_List[1].Item_Code  := LNM$_String;
    Item_List[1].Buffer_Addr  := IADDRESS(Translated_Name.Body);
    Item_List[1].Return_Addr  := IADDRESS(Translated_Name.Length);

    { Terminate the item list }
    Item_List[2].Terminator  := 0;

    { Translate the CYGNUS logical name }
    Status := $trnlnm(Tabnam := 'LNM$FILE_DEV', Lognam := 'CYGNUS',  5
        Itmlst := Item_List);
    IF NOT ODD(Status)  6
    THEN
        LIB$SIGNAL(Status)
    ELSE
        WRITELN('CYGNUS is equivalent to ',Translated_Name);

    END.

Pascal Notes

1

The Pascal environment file STARLET.PEN defines OpenVMS system services, data structures and constants. PASCAL$LIB_ROUTINES declares the LIB$ Run-Time Library routines.

2

The structure of an item list entry is defined using a variant record type.

3

The VARYING OF CHAR type is a variable-length character string with two components: a word-integer length and a character string body, which in this example is 255 bytes long. The VOLATILE attribute is required for variables that are modified by means other than a direct assignment or as an argument in a routine call.

4

The functions SIZE and IADDRESS obtain the allocated size of the string body and the address of the string body and length. The returned length will be stored into the length field of the varying string Translated_Name, so that it will appear to be the correct size.

5

The definition of the SYS$TRNLNM routine in STARLET.PEN contains specifications of the passing mechanism to be used for each argument;thus, it is not necessary to specify the mechanism here.

6

The IF statement performs a logical test following the function reference to see if the service completed successfully. If an error or warning occurs during the service call, the error is signaled.

Example 4.9. System Service Call in VAX MACRO
CYGDES: .ASCID  /CYGNUS/ 1            ; Descriptor for CYGNUS string
TBLDES: .ASCID  /LNM$FILE_DEV/ 2      ; Logical name table
NAMBUF: .BLKB   255  3               ; Output buffer
NAMLEN: .BLKW   1    4               ; Word to receive length
ITEMS:  .WORD   255                    ; Output buffer length
        .WORD   LNM$STRING            ; Item code
        .ADDRESS -                    ; Output buffer
                NAMBUF
        .ADDRESS -                    ; Return length
                NAMLEN
        .LONG   0                     ; List terminator
          .
          .
          .
        .ENTRY  ORION,0 5             ; Routine entry point & mask
        $TRNLNM_S -     6
                TABNAM=TBLDES, -
                LOGNAM=CYGDES, -
                ITMLST=ITEMS
        BLBC   R0,ERROR 7             ; Check for error
         .
         .
         .
        .END

VAX MACRO Notes

1

The input character string descriptor argument is defined using the .ASCID directive.

2

The name of the table to search is defined using the .ASCID directive.

3

Enough bytes to hold the output data are allocated for an output character string argument.

4

The MACRO directive .BLKW reserves a word to hold the output length.

5

A routine name and entry mask show the beginning of executable code in a routine or subroutine.

6

A macro name that has the suffix _S or _G calls the service. You can specify arguments either by keyword (as in this example) or by positional order. (Keyword names correspond to the names of the arguments shown in lowercase in the system service format descriptions in the VSI OpenVMS System Services Reference Manual). If you omit any optional arguments (if you accept the defaults), you can omit them completely if you specify arguments by keyword. If you specify arguments by positional order, however, you must specify the comma for each missing argument. Use the number sign (#) to indicate a literal value for an argument.

7

The BLBC instruction causes a branch to a subroutine named ERROR (not shown) if the low bit of the condition value returned from the service is clear (low bit clear = failure or warning). You can use a BSBW instruction to branch unconditionally to a routine that checks the return status.

Chapter 5. STARLET Structures and Definitions for C Programmers

This chapter describes the libraries that contain C header files for routines supplied by the OpenVMS Alpha and OpenVMS I64 operating systems.

5.1. SYS$STARLET_C.TLB Equivalency to STARLETSD.TLB

The SYS$STARLET_C.TLB file, which was introduced in OpenVMS Alpha Version 1.0, contains all the .H files that provide STARLET functionality equivalent to STARLETSD.TLB. The file SYS$STARLET_C.TLB, together with DECC$RTLDEF.TLB that ships with the VSI C Compiler, replaces VAXCDEF.TLB that previously shipped with the VAX C Compiler. DECC$RTLDEF.TLB contains all the .H files that support the compiler and RTL, such as STDIO.H.

If you are running an application from a release prior to OpenVMS Alpha Version 1.0, the following differences may require source changes:
  • RMS structures

    Previously, the RMS structures FAB, NAM, RAB, XABALL, and so forth, were defined in the appropriate .H files as struct RAB {..., for example. The .H files supplied in OpenVMS Alpha Version 1.0 define them as struct rabdef {.... To compensate for this difference, lines of the form #define RAB rabdef have been added. However, there is one situation where a source change is required because of this change. If you have a private structure that contains a pointer to one of these structures and your private structure is defined (but not used) before the RMS structure has been defined, you will receive compile-time errors similar to the following:
    %CC-E-PASNOTMEM, In this statement, "rab$b_rac" is not a member of "rab".

    This error can be avoided by reordering your source file so that the RMS structure is defined before the private structure. Typically, this involves moving around #include statements.

  • LIB (privileged interface) structures

    Historically, three structures from LIB (NFBDEF.H, FATDEF.H, and FCHDEF.H) have been made available as .H files. These files were shipped as .H files in OpenVMS Alpha Version 1.0 and 1.5 (not in the new SYS$STARLET_C.TLB). As of OpenVMS Alpha Version 7.0, the file SYS$LIB_C.TLB, containing all LIB structures and definitions, was added. These three .H files are now part of that .TLB and are no longer shipped separately. Source changes may be required, because no attempt has been made to preserve any existing anomalies in these files. The structures and definitions from LIB are for privileged interfaces only and are therefore subject to change.

  • Use of variant_struct and variant_union

    In the new .H files, variant_struct and variant_union are always used; whereas previously some structures used struct and union. Therefore, the intermediate structure names cannot be specified when referencing fields within data structures.

    For example, the following statement:
    AlignFaultItem.PC[0] = DataPtr->afr$r_pc_data_overlay.afr$q_fault_pc[0];
    becomes:
    AlignFaultItem.PC[0] = DataPtr->afr$q_fault_pc[0];
  • Member alignment

    Each of the .H files in SYS$STARLET_C.TLB saves and restores the state of #pragma member_alignment.

  • Conventions The .H files in SYS$STARLET_C.TLB adhere to some conventions that were only partly followed in VAXCDEF.TLB. All constants (#defines) have uppercase names. All identifiers (routines, structure members, and so forth) have lowercase names. Where there is a difference from VAXCDEF.TLB, the old symbol name is also included for compatibility, but users are encouraged to follow the new conventions.

  • Use of Librarian utility to access the .H files

    During installation of OpenVMS Alpha, the contents of SYS$STARLET_C.TLB are not extracted into the separate .H files. The VSI C Compiler accesses these files from within SYS$STARLET_C.TLB, regardless of the format of the #include statement. If you want to inspect an individual .H file, you can use the Librarian utility, as in the following example:
    $ LIBRARY /EXTRACT=AFRDEF /OUTPUT=AFRDEF.H SYS$LIBRARY:SYS$STARLET_C.TLB
  • Additional .H files included in SYS$STARLET_C.TLB

    In addition to the .H files derived from STARLET sources, SYS$STARLET_C.TLB includes .H files that provide support for POSIX Threads Library, such as CMA.H.

5.2. NEW STARLET Definitions for C

SYS$LIBRARY:SYS$STARLET_C.TLB (or STARLET) provides C function prototypes for system services, as well as data structure definitions. The compiler searches the library file SYS$LIBRARY:SYS$STARLET_C.TLB for the STARLET header files. The definitions are consistent with the OpenVMS C language coding conventions and definitions (typedefs) used in SYS$LIBRARY:SYS$LIB_C.TLB.

To maintain source compatibility for users of STARLET.H as provided prior to OpenVMS Alpha Version 7.0, the old style function declarations and definitions are still provided by default. To take advantage of the new system service function prototypes and type definitions, you must explicitly enable them.

You can define the __NEW_STARLET symbol with a VSI C command line qualifier or include the definition directly in your source program. For example:
  • Define the _NEW_STARLET symbol with the VSI C command line qualifier as follows:
    /DEFINE=(__NEW_STARLET=1)

    or

  • Define the _NEW_STARLET symbol in your C source program before including the SYS$STARLET_C.TLB header files:
    #define __NEW_STARLET 1
    
    #include  <starlet.h>
    #include  <vadef.h>
To see the available system service function prototypes in STARLET.H, you can use the Librarian utility as shown in the following example:
$ LIBRARY/OUTPUT=STARLET.H SYS$LIBRARY:SYS$STARLET_C.TLB/EXTRACT=STARLET
The following example shows a new system service function prototype as it is defined in STARLET.H:
    #pragma __required_pointer_size __long

     int sys$expreg_64(
               struct _generic_64 *region_id_64,
               unsigned __int64 length_64,
               unsigned int acmode,
               unsigned int flags,
               void *(*(return_va_64)),
               unsigned __int64 *return_length_64);

    #pragma __required_pointer_size __short

For more information about VSI C pointer size pragmas, see the VSI C User Manual.

The following source code example shows the sys$expreg_64 function prototype referenced in a program.
#define __NEW_STARLET 1               /* Enable "New Starlet" features */

#include <starlet.h>                  /* Declare prototypes for system services */
#include <gen64def.h>                 /* Define GENERIC_64 type */
#include <vadef.h>                    /* Define VA$ constants */

#include <ints.h>                     /* Define 64-bit integer types */
#include <far_pointers.h>             /* Define 64-bit pointer types */

{
    int status;                       /* Ubiquitous VMS status value */
    GENERIC_64 region = { VA$C_P2 };  /* Expand in "default" P2 region */
    VOID_PQ p2_va;                    /* Returned VA in P2 space */
    uint64 length;                    /* Allocated size in bytes */
    extern uint64 page_size;          /* Page size in bytes */

    status = sys$expreg_64( &region, request_size, 0, 0, &p2_va, &length );
    ...

}
Table 5.1 lists the data structures that are used by the new function protypes.
Table 5.1. Structures Used by _NEW_STARLET Prototypes

Structure Used by Prototype

Defined by Header File

Common Prefix for Structure Member Names

Description

struct _acmecb

acmedef.h

acmedef$

ACM communications buffer

struct _acmesb

acmedef.h

acmedef$

ACM status block

struct _cluevthndl

cluevtdef.h

cluevthndl$

Cluster event handle

struct _fabdef

fabdef.h

fab$

File access block

struct _generic_64

gen64def.h

gen64$

Generic quadword structure

struct _ieee

ieeedef.h

ieee$

IEEE Floating point control structure

struct _ile2?

iledef.h

ile2$

Item list entry 2

struct _ile3?

iledef.h

ile3$

Item list entry 3

struct _ilea_64?

iledef.h

ilea_64$

64-bit item list entry A structure

struct _ileb_64?

iledef.h

ileb_64$

64-bit item list entry B structure

struct _iosa

iosadef.h

iosa$

I/O status area

struct _iosb

iosbdef.h

iosb$

I/O status block

struct _lksb

lksbdef.h

lksb$

Lock status block

struct _rabdef

rabdef.h

rab$

RMS record access block

struct _secid

seciddef.h

secid$

Global section identifier

struct _va_range

va_rangedef.h

va_range$

32-bit virtual address range

Part II. I/O, System, and Programming Routines

This part of this second volume describes the I/O operations, and the system and programming routines used by run-time libraries and system services.

Chapter 6. Run-Time Library Input/Output Operations

This chapter describes the different I/O programming capabilities provided by the run-time library and illustrates these capabilities with examples of common I/O tasks.

6.1. Choosing I/O Techniques

The operating system and its compilers provide the following methods for completing input and output operations within a program:
  • DEC Text Processing Utility

  • DECforms software

  • Program language I/O statements

  • OpenVMS Record Management Services (RMS) and Run-Time Library (RTL) routines

  • SYS$QIO and SYS$QIOW system services

  • Third party-supplied device drivers to control the I/O to the device itself

The DEC Text Processing Utility (DECTPU) is a text processor that can be used to create text editing interfaces. DECTPU has the following features:
  • High-level procedure language with several data types, relational operators, error interception, looping, case language statements, and built-in procedures

  • Compiler for the DECTPU procedure language

  • Interpreter for the DECTPU procedure language

  • Extensible Versatile Editor (EVE) editing interface which, in addition to the EVE keypad, provides EDT, VT100, WPS, and numeric keypad emulation

In addition, DECTPU offers the following special features:
  • Multiple buffers

  • Multiple windows

  • Multiple subprocesses

  • Text processing in batch mode

  • Insert or overstrike text entry

  • Free or bound cursor motion

  • Learn sequences

  • Pattern matching

  • Key definition

The method you select for I/O operations depends on the task you want to accomplish, ease of use, speed, and level of control you want.

The VSI DECforms for OpenVMS software is a forms management product for transaction processing. DECforms integrates text and graphics into forms and menus that application programs use as an interface to users. DECforms software offers application developers software development tools and a run-time environment for implementing interfaces.

DECforms software integrates with the Application Control and Management System (ACMS), a transaction process (TP) monitor that works with other commercial applications to provide complete customizable development and run-time environments for TP applications. An asynchronous call interface to ACMS allows a single DECforms run-time process to control multiple terminals simultaneously in a multithreaded way, resulting in an efficient use of memory. By using the ACMS Remote Access Option, DECforms software can be distributed to remote CPUs. This technique allows the host CPU to offload forms processing and distribute it as closely as possible to the end user.

In contrast to OpenVMS RMS, RTLs, SYS$QIOs, and device driver I/O, program language I/O statements have the slowest speed and lowest level of control, but they are the easiest to use and are highly portable.

OpenVMS RMS and RTL routines can perform most I/O operations for a high-level or assembly language program. For information about OpenVMS RMS, see the VSI OpenVMS Record Management Services Reference Manual.

System services can complete any I/O operation and can access devices not supported within OpenVMS RMS. See Chapter 7 for a description of using I/O system services.

Writing a device driver provides the most control over I/O operations, but can be more complex to implement. For information about device drivers for VAX systems, see the OpenVMS VAX Device Support Manual.

Several types of I/O operations can be performed within a program, including the following:
  • RTL routines allow you either to read simple input from a user or send simple output to a user. One RTL routine allows you to specify a string to prompt for input from the current input device, defined by SYS$INPUT. Another RTL routine allows you to write a string to the current output device, defined by SYS$OUTPUT. See Section 6.2 and Section 6.3 for more information.

  • RTL routines allow you either to read complex input from a user or to send complex output to a user. By providing an extensive number of screen management (SMG$) routines, the RTL allows you either to read multiple lines of input from users or to send complex output to users. The SMG$ routines also allow you to create and modify complicated displays that accept input and produce output. See Section 6.4 for more information.

  • RTL routines allow you to use programming language I/O statements to send data to and receive data from files. Program language I/O statements call OpenVMS RMS routines to complete most file I/O. You can also use OpenVMS RMS directly in your programs for accomplishing file I/O. See Chapter 12 for more information.

  • The SYS$QIO and SYS$QIOW system services allow you to send data to and from devices with the most flexibility and control. You can use system services to access devices not supported by your programming language or by OpenVMS RMS.

    You can perform other special I/O actions, such as using interrupts, controlling echo, handling unsolicited input, using the type-ahead buffer, using case conversion, and sending system broadcast messages, by using SMG$ routines or, for example, by using SYS$BRKTHRU system service to broadcast messages. See Section 6.5 for more information.

6.2. Using SYS$INPUT and SYS$OUTPUT

Typically, you set up your program so that the user is the invoker. The user starts the program either by entering a DCL command associated with the program or by using the RUN command.

6.2.1. Default Input and Output Devices

The user's input and output devices are defined by the logical names SYS$INPUT and SYS$OUTPUT, which are initially set to the values listed in Table 6.1.
Table 6.1. SYS$INPUT and SYS$OUTPUT Values
Logical NameUser ModeEquivalence Device or File

SYS$INPUT

Interactive

Terminal at which the user is logged in

Batch job

Data lines following the invocation of the program

Command procedure

Data lines following the invocation of the program

SYS$OUTPUT

Interactive

Terminal at which the user is logged in

Batch job

Batch log file

Command procedure

Terminal at which the user is logged in

Generally, use of SYS$INPUT and SYS$OUTPUT as the primary input and output devices is recommended. A user of the program can redefine SYS$INPUT and SYS$OUTPUT to redirect input and output as desired. For example, the interactive user might redefine SYS$OUTPUT as a file name to save output in a file rather than display it on the terminal.

6.2.2. Reading and Writing to Alternate Devices and External Files

Alternatively, you can design your program to read input from and write output to a file or a device other than the user's terminal. Files may be useful for writing large amounts of data, for writing data that the user might want to save, and for writing data that can be reused as input. If you use files or devices other than SYS$INPUT and SYS$OUTPUT, you should provide the names of the files or devices (best form is to use logical names) and any conventions for their use. You can specify such information by having the program write it to the terminal, by creating a help file, or by providing user documentation.

6.3. Working with Simple User I/O

Usually, you can request information from or provide information to the user with little regard for formatting. For such simple I/O, use either LIB$GET_INPUT and LIB$PUT_OUTPUT or the I/O statements for your programming language.

To provide complex screen displays for input or output, use the screen management facility described in Section 6.4.

6.3.1. Default Devices for Simple I/O

The LIB$GET_INPUT and LIB$PUT_OUTPUT routines read from SYS$INPUT and write to SYS$OUTPUT, respectively. The logical names SYS$INPUT and SYS$OUTPUT are implicit to the routines, because you need only call the routine to access the I/O unit (device or file) associated with SYS$INPUT and SYS$OUTPUT. You cannot use these routines to access an I/O unit other than the one associated with SYS$INPUT or SYS$OUTPUT.

6.3.2. Getting a Line of Input

A read operation transfers one record from the input unit to a variable or variables of your choice. At a terminal, the user ends a record by pressing a terminator. The terminators are the ASCII characters NUL through US (0 through 31) except for LF, VT, FF, TAB, and BS. The usual terminator is CR (carriage return), which is generated by pressing the Return key.

If you are reading character data, LIB$GET_INPUT is a simple way of prompting for and reading the data. If you are reading noncharacter data, programming language I/O statements are preferable since they allow you to translate the data to a format of your choice.

For example, Fortran offers the ACCEPT statement, which reads data from SYS$INPUT, and the READ statement, which reads data from an I/O unit of your choice.

Make sure the variables that you specify can hold the largest number of characters the user of your program might enter, unless you want to truncate the input deliberately. Overflowing the input variable using LIB$GET_INPUT causes the fatal error LIB$_INPSTRTRU (defined in $LIBDEF); overflowing the input variable using language I/O statements may not cause an error but does truncate your data.

LIB$GET_INPUT places the characters read in a variable of your choice. You must define the variable type as a character. Optionally, LIB$GET_INPUT places the number of characters read in another variable of your choice. For input at a terminal, LIB$GET_INPUT optionally writes a prompt before reading the input. The prompt is suppressed automatically for an operation not taking place at a terminal.

Example 6.1 uses LIB$GET_INPUT to read a line of input.
Example 6.1. Reading a Line of Data
INTEGER*4     STATUS, 
2             LIB$GET_INPUT 
INTEGER*2     INPUT_SIZE 
CHARACTER*512 INPUT 
STATUS = LIB$GET_INPUT (INPUT,           ! Input value 
2                       'Input value: ', ! Prompt (optional) 
2                       INPUT_SIZE)      ! Input size (optional) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.3.3. Getting Several Lines of Input

The usual technique for obtaining a variable number of input records—either values for which you are prompting or data records from a file—is to read and process records until the end-of-file. End-of-file means one of the following:
  • Terminal—The user has pressed Ctrl/Z. To ensure that the convention is followed, you might first write a message telling the user to press Ctrl/Z to terminate the input sequence.

  • Command procedure—The end of a sequence of data lines has been reached. That is, a sequence of data lines ends at the next DCL command (a line in the procedure beginning with a dollar sign [$]) or at the end of the command procedure file.

  • File—The end of an actual file has been reached.

Process the records in a loop (one record per iteration) and terminate the loop on end-of-file. LIB$GET_INPUT returns the error RMS$_EOF (defined in $RMSDEF) when end-of-file occurs.

Example 6.2 uses a Fortran READ statement in a loop to read a sequence of integers from SYS$INPUT.
Example 6.2. Reading a Varying Number of Input Records
! Return status and error codes 
INTEGER   STATUS, 
2         IOSTAT, 
3         STATUS_OK, 
4         IOSTAT_OK 
PARAMETER (STATUS_OK = 1, 
2          IO_OK = 0) 
INCLUDE   '($FORDEF)' 
! Data record read on each iteration 
INTEGER   INPUT_NUMBER 
! Accumulated data records 
INTEGER   STORAGE_COUNT, 
2         STORAGE_MAX 
PARAMETER (STORAGE_MAX = 255) 
INTEGER    STORAGE_NUMBER (STORAGE_MAX) 
! Write instructions to interactive user 
TYPE *, 
2 'Enter values below. Press CTRL/Z when done.' 
! Get first input value 
WRITE (UNIT=*, 
2      FMT='(A,$)') ' Input value: ' 
READ (UNIT=*, 
2     IOSTAT=IOSTAT, 
2     FMT='(BN,I)') INPUT_NUMBER 
IF (IOSTAT .EQ. IO_OK) THEN 
  STATUS = STATUS_OK 
ELSE 
  CALL ERRSNS (,,,,STATUS) 
END IF 
! Process each input value until end-of-file 
DO WHILE ((STATUS .NE. FOR$_ENDDURREA) .AND. 
          (STORAGE_COUNT .LT. STORAGE_MAX)) 
  ! Keep repeating on conversion error 
  DO WHILE (STATUS .EQ. FOR$_INPCONERR) 
    WRITE (UNIT=*, 
2          FMT='(A,$)') ' Try again: ' 
    READ (UNIT=*, 
2         IOSTAT=IOSTAT, 
2         FMT='(BN,I)') INPUT_NUMBER 
    IF (IOSTAT .EQ. IO_OK) THEN 
      STATUS = STATUS_OK 
    ELSE 
      CALL ERRSNS (,,,,STATUS) 
    END IF 
  END DO 
  ! Continue if end-of-file not entered 
  IF (STATUS .NE. FOR$_ENDDURREA) THEN 
    ! Status check on last read 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    ! Store input numbers in input array 
    STORAGE_COUNT = STORAGE_COUNT + 1 
    STORAGE_NUMBER (STORAGE_COUNT) = INPUT_NUMBER 
    ! Get next input value 
    WRITE (UNIT=*, 
2          FMT='(A,$)') ' Input value: ' 
    READ (UNIT=*, 
2         IOSTAT=IOSTAT, 
2         FMT='(BN,I)') INPUT_NUMBER 
    IF (IOSTAT .EQ. IO_OK) THEN 
      STATUS = STATUS_OK 
    ELSE 
      CALL ERRSNS (,,,,STATUS) 
    END IF 
  END IF 
END DO

6.3.4. Writing Simple Output

You can use LIB$PUT_OUTPUT to write character data. If you are writing noncharacter data, programming language I/O statements are preferable because they allow you to translate the data to a format of your choice.

LIB$PUT_OUTPUT writes one record of output to SYS$OUTPUT. Typically, you should avoid writing records that exceed the device width. The width of a terminal is 80 or 132 characters, depending on the setting of the physical characteristics of the device. The width of a line printer is 132 characters. If your output record exceeds the width of the device, the excess characters are either truncated or wrapped to the next line, depending on the setting of the physical characteristics.

You must define a value (a variable, constant, or expression) to be written. The value must be expressed in characters. You should specify the exact number of characters being written and not include the trailing portion of a variable.

The following example writes a character expression to SYS$OUTPUT:
INTEGER*4    STATUS, 
2            LIB$PUT_OUTPUT 
CHARACTER*40 ANSWER 
INTEGER*4    ANSWER_SIZE 
   .
   .
   .
STATUS = LIB$PUT_OUTPUT ('Answer: ' // ANSWER (1:ANSWER_SIZE)) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.4. Working with Complex User I/O

The following sections present VSI DECwindows Motif for OpenVMS (DECwindows Motif), and the SMG$ run-time library routines that enable complex screen display I/O.

6.4.1. VSI DECwindows Motif

The VSI DECwindows Motif environment provides a consistent user interface for developing software applications. It also includes an extensive set of programming libraries and tools. The following VSI DECwindows Motif software allows you to build a graphical user interface:
  • Toolkit composed on graphical user interface objects, such as widgets and gadgets. Widgets provide advanced programming capabilities that permit you to create graphic applications easily; gadgets, similar to widgets, require less memory to create labels, buttons, and separators.

  • Language to describe visual aspects of objects, such as menus, labels, and forms, and to specify changes resulting from user interaction.

  • OSF/Motif Window Manager to allow you to customize the interface.

VSI DECwindows Motif environment also makes available the LinkWorks services for creating, managing, and traversing informational links between different application-specific data. Along with the LinkWorks Manager application, LinkWorks services help organize information into a hyperinformation environment. LinkWorks Developer's Tools provide a development environment for creating, modifying, and maintaining hyperapplications.

6.4.1.1. DECwindows Server Height or Width Exceeding 32767 (VAX Only)

On OpenVMS VAX systems, when an X application sends the display server a width or height greater than 32767, the application may terminate with a BadValue error similar to the following:
 X error event received from server: BadValue (integer parameter out of 
 range for operation) 
   Major opcode of failed request: 61 (X_ClearArea) 
   Value in failed request: 0xffff**** 
   Serial number of failed request: ### 
   Current serial number in output stream: ###
The following calls can cause this problem:
  • CopyArea()
  • CreateWindow ()
  • PutImage()
  • GetImage()
  • CopyPlane()
  • ClearArea()

This is due to the width and height being defined as a signed word by the display server when it should be defined as an unsigned word (CARD16) that allows for values up to 65536.

To modify the default operation, perform the following steps:
  1. Set the logical name DECW$CARD16_VALIDATE to TRUE as follows:
    $DEFINE/TABLE=DECW$SERVER0_TABLE   DECW$CARD16_VALIDATE  TRUE
  2. Exit the session and log back in.

    Exiting the session causes the display server to reset using the new value of the logical name DECW$CARD16_VALIDATE. The server will now accept values that are greater than 32767 without generating an error.

To make this a permanent change, add the command from step 1 to the file SYS$MANAGER:DECW$PRIVATE_SERVER_SETUP.COM.

6.4.2. SMG$ Run-Time Routines

The SMG$ run-time library routines provide a simple, device-independent interface for managing the appearance of the terminal screen. The SMG$ routines are primarily for use with video terminals; however, they can be used with files or hardcopy terminals.

To use the screen management facility for output, do the following:
  1. Create a pasteboard—A pasteboard is a logical, two-dimensional area on which you place virtual displays. Use the SMG$CREATE_PASTEBOARD routine to create a pasteboard, and associate it with a physical device. When you refer to the pasteboard, SMG performs the necessary I/O operation to the device.

  2. Create a virtual display—A virtual display is a logical, two-dimensional area in which you place the information to be displayed. Use the SMG$CREATE_VIRTUAL_DISPLAY routine to create a virtual display.

  3. Paste virtual displays to the pasteboard—To make a virtual display visible, map (or paste) it to the pasteboard using the SMG$PASTE_VIRTUAL_DISPLAY routine. You can reference a virtual display regardless of whether that display is currently pasted to a pasteboard.

  4. Create a viewport for a virtual display—A view port is a rectangular viewing area that can be moved around on a buffer to view different pieces of the buffer. The viewport is associated with a virtual display.

Example 6.3 associates a pasteboard with the terminal, creates a virtual display the size of the terminal screen, and pastes the display to the pasteboard. When text is written to the virtual display, the text appears on the terminal screen.
Example 6.3. Associating a Pasteboard with a Terminal
   .
   .
   .
! Screen management control structures 
INTEGER*4 PBID,   ! Pasteboard ID 
2         VDID,   ! Virtual display ID 
2         ROWS,   ! Rows on screen 
2         COLS    ! Columns on screen 
! Status variable and routines called as functions 
INTEGER*4 STATUS, 
2         SMG$CREATE_PASTEBOARD, 
2         SMG$CREATE_VIRTUAL_DISPLAY, 
2         SMG$PASTE_VIRTUAL_DISPLAY 
! Set up SYS$OUTPUT for screen management 
! and get the number of rows and columns on the screen 
STATUS = SMG$CREATE_PASTEBOARD (PBID,    ! Return value 
2                               'SYS$OUTPUT', 
2                               ROWS,    ! Return value 
2                               COLUMNS) ! Return value 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Create virtual display that pastes to the full screen size 
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (ROWS, 
2                                    COLUMNS, 
2                                    VDID) ! Return value           
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Paste virtual display to pasteboard 
STATUS = SMG$PASTE_VIRTUAL_DISPLAY (VDID, 
2                                   PBID, 
2                                   1, ! Starting at row 1 and 
2                                   1) ! column 1 of the screen 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .

To use the SMG$ routines for input, you associate a virtual keyboard with a physical device or file using the SMG$CREATE_VIRTUAL_KEYBOARD routine. The SMG$input routines can be used alone or with the output routines. This section assumes that you are using the input routines with the output routines. Section 6.5 describes how to use the input routines without the output routines.

The screen management facility keeps an internal representation of the screen contents; therefore, it is important that you do not mix SMG$ routines with other forms of terminal I/O. The following subsections contain guidelines for using most of the SMG$ routines; for more details, see the VSI OpenVMS RTL Screen Management (SMG$) Manual.

6.4.3. Pasteboards

Use the SMG$CREATE_PASTEBOARD routine to create a pasteboard and associate it with a physical device. SMG$CREATE_PASTEBOARD returns a unique pasteboard identification number; use that number to refer to the pasteboard in subsequent calls to SMG$ routines. After associating a pasteboard with a device, your program references only the pasteboard. The screen management facility performs all necessary operations between the pasteboard and the physical device. Example 6.4 creates a pasteboard.
Example 6.4. Creating a Pasteboard
STATUS = SMG$CREATE_PASTEBOARD (PBID, ROWS, COLUMNS) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.4.3.1. Erasing a Pasteboard

When you create a pasteboard, the screen management facility clears the screen by default. To clear the screen yourself, invoke the SMG$ERASE_PASTEBOARD routine. Any virtual displays associated with the pasteboard are removed from the screen, but their contents in memory are not affected. The following example erases the screen:
STATUS = SMG$ERASE_PASTEBOARD (PBID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.4.3.2. Deleting a Pasteboard

Invoking the SMG$DELETE_PASTEBOARD routine deletes a pasteboard, making the screen unavailable for further pasting. The optional second argument of the SMG$DELETE_PASTEBOARD routine allows you to indicate whether the routine clears the screen (the default) or leaves it as is. The following example deletes a pasteboard and clears the screen:
STATUS = SMG$DELETE_PASTEBOARD (PBID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

By default, the screen is erased when you create a pasteboard. Generally, you should erase the screen at the end of a session.

6.4.3.3. Setting Screen Dimensions and Background Color

The SMG$CHANGE_PBD_CHARACTERISTICS routine sets the dimensions of the screen and its background color. You can also use this routine to retrieve dimensions and background color. To get more detailed information about the physical device, use the SMG$GET_PASTEBOARD_ATTRIBUTES routine. Example 6.5changes the screen width to 132 and the background to white, then restores the original width and background before exiting.
Example 6.5. Modifying Screen Dimensions and Background Color
   .
   .
   .
INTEGER*4 WIDTH, 
2         COLOR 
INCLUDE   '($SMGDEF)' 
! Get current width and background color 
STATUS = SMG$CHANGE_PBD_CHARACTERISTICS (PBID,, 
2                                        WIDTH,,,, 
2                                        COLOR) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Change width and background color 
STATUS = SMG$CHANGE_PBD_CHARACTERISTICS (PBID, 
2                                        132,,,, 
2                                        SMG$C_COLOR_WHITE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .
! Restore width and background color 
STATUS = SMG$CHANGE_PBD_CHARACTERISTICS (PBID, 
2                                        WIDTH,,,, 
2                                        COLOR) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.4.4. Virtual Displays

You write to virtual displays, which are logically configured as rectangles, by using the SMG$ routines. The dimensions of a virtual display are designated vertically as rows and horizontally as columns. A position in a virtual display is designated by naming a row and a column. Row and column numbers begin at 1.

6.4.4.1. Creating a Virtual Display

Use the SMG$CREATE_VIRTUAL_DISPLAY routine to create a virtual display. SMG$CREATE_VIRTUAL_DISPLAY returns a unique virtual display identification number; use that number to refer to the virtual display.

Optionally, you can use the fifth argument of SMG$CREATE_VIRTUAL_DISPLAY to specify one or more of the following video attributes: blinking, balding, reversing background, and underlining. All characters written to that display will have the specified attribute unless you indicate otherwise when writing text to the display. The following example makes everything written to the display HEADER_VDID appear bold by default:
INCLUDE '($SMGDEF)' 
   .
   .
   .
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (1,   ! Rows 
2                                    80,  ! Columns 
2                                    HEADER_VDID,, 
2                                    SMG$M_BOLD)
You can border a virtual display by specifying the fourth argument when you invoke SMG$CREATE_VIRTUAL_DISPLAY. You can label the border with the routine SMG$LABEL_BORDER. If you use a border, you must leave room for it: a border requires two rows and two columns more than the size of the display. The following example places a labeled border around the STATS_VDID display. As pasted, the border occupies rows 2 and 13 and columns 1 and 57.
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (10,  ! Rows 
2                                    55,  ! Columns 
2                                    STATS_VDID, 
2                                    SMG$M_BORDER) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$LABEL_BORDER (STATS_VDID, 
2                          'statistics') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$PASTE_VIRTUAL_DISPLAY (STATS_VDID, 
2                                   PBID, 
2                                   3,  ! Row 
2                                   2)  ! Column

6.4.4.2. Pasting Virtual Displays

To make a virtual display visible, paste it to a pasteboard using the SMG$PASTE_VIRTUAL_DISPLAY routine. You position the virtual display by specifying which row and column of the pasteboard should contain the upper left corner of the display. Example 6.6 defines two virtual displays and pastes them to one pasteboard.
Example 6.6. Defining and Pasting a Virtual Display
   .
   .
   .
INCLUDE '($SMGDEF)' 
INTEGER*4 PBID, 
2         HEADER_VDID, 
2         STATS_VDID 
INTEGER*4 STATUS, 
2         SMG$CREATE_PASTEBOARD, 
2         SMG$CREATE_VIRTUAL_DISPLAY, 
2         SMG$PASTE_VIRTUAL_DISPLAY 
! Create pasteboard for SYS$OUTPUT 
STATUS = SMG$CREATE_PASTEBOARD (PBID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Header pastes to first rows of screen 
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (3,             ! Rows 
2                                    78,            ! Columns 
2                                    HEADER_VDID,   ! Name 
2                                    SMG$M_BORDER)  ! Border 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$PASTE_VIRTUAL_DISPLAY (HEADER_VDID, 
2                                   PBID, 
2                                   2,              ! Row 
2                                   2)              ! Column 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Statistics area pastes to rows 5 through 15, 
! columns 2 through 56 
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (10,            ! Rows 
2                                    55,            ! Columns 
2                                    STATS_VDID,    ! Name 
2                                    SMG$M_BORDER)  ! Border 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$PASTE_VIRTUAL_DISPLAY (STATS_VDID, 
2                                   PBID, 
2                                   5,              ! Row 
2                                   2)              ! Column 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .

Figure 6.1 shows the screen that results from Example 6.6.

Figure 6.1. Defining and Pasting Virtual Displays
Defining and Pasting Virtual Displays

You can paste a single display to any number of pasteboards. Any time you change the display, all pasteboards containing the display are automatically updated.

A pasteboard can hold any number of virtual displays. You can paste virtual displays over one another to any depth, occluding the displays underneath. The displays underneath are only occluded to the extent that they are covered;that is, the parts not occluded remain visible on the screen. (In Figure 6.2, displays 1 and 2 are partially occluded). When you unpaste a virtual display that occludes another virtual display, the occluded part of the display underneath becomes visible again.

You can find out whether a display is occluded by using the SMG$CHECK_FOR_OCCLUSION routine. The following example pastes a two-row summary display over the last two rows of the statistics display, if the statistics display is not already occluded. If the statistics display is occluded, the example assumes that it is occluded by the summary display and unpastes the summary display, making the last two rows of the statistics display visible again.
  STATUS = SMG$CHECK_FOR_OCCLUSION (STATS_VDID, 
2                                   PBID, 
2                                   OCCLUDE_STATE) 
! OCCLUDE_STATE must be defined as INTEGER*4 
  IF (OCCLUDE_STATE) THEN 
    STATUS = SMG$UNPASTE_VIRTUAL_DISPLAY (SUM_VDID, 
2                                         PBID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  ELSE 
    STATUS = SMG$PASTE_VIRTUAL_DISPLAY (SUM_VDID, 
2                                       PBID, 
2                                       11, 
2                                       2) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  END IF

6.4.4.3. Rearranging Virtual Displays

Pasted displays can be rearranged by moving or repasting.
  • Moving—To move a display, use the SMG$MOVE_VIRTUAL_DISPLAY routine. The following example moves display 2. Figure 6.2 shows the screen before and after the statement executes.
    STATUS = SMG$MOVE_VIRTUAL_DISPLAY (VDID, 
    2                                  PBID, 
    2                                  5, 
    2                                  10) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
    Figure 6.2. Moving a Virtual Display
    Moving a Virtual Display
  • Repasting—To repaste a display, use the SMG$REPASTE_VIRTUAL_DISPLAY routine. The following example repastes display 2. Figure 6.3 shows the screen before and after the statement executes.
    STATUS = SMG$REPASTE_VIRTUAL_DISPLAY (VDID, 
    2                                     PBID, 
    2                                     4, 
    2                                     4) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
    Figure 6.3. Repasting a Virtual Display
    Repasting a Virtual Display

You can obtain the pasting order of the virtual displays using SMG$LIST_PASTING_ORDER. This routine returns the identifiers of all the virtual displays pasted to a specified pasteboard.

6.4.4.4. Removing Virtual Displays

You can remove a virtual display from a pasteboard in a number of different ways:
  • Erase a virtual display—Invoking SMG$UNPASTE_VIRTUAL_DISPLAY erases a virtual display from the screen but retains its contents in memory. The following example erases the statistics display:
    STATUS = SMG$UNPASTE_VIRTUAL_DISPLAY (STATS_VDID, 
    2                                     PBID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
  • Delete a virtual display—Invoking SMG$DELETE_VIRTUAL_DISPLAY removes a virtual display from the screen and removes its contents from memory. The following example deletes the statistics display:
    STATUS = SMG$DELETE_VIRTUAL_DISPLAY (STATS_VDID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
  • Delete several virtual displays—Invoking SMG$POP_VIRTUAL_DISPLAY removes a specified virtual display and any virtual displays pasted after that display from the screen and removes the contents of those displays from memory. The following example pops display 2. Figure 6.4 shows the screen before and after popping. (Note that display 3 is deleted because it was pasted after display 2, and not because it is occluding display 2).
    STATUS = SMG$POP_VIRTUAL_DISPLAY (STATS_VDID, 
    2                                 PBID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
    Figure 6.4. Popping a Virtual Display
    Popping a Virtual Display

6.4.4.5. Modifying a Virtual Display

The screen management facility provides several routines for modifying the characteristics of an existing virtual display:
  • SMG$CHANGE_VIRTUAL_DISPLAY—Changes the size, video attributes, or border of a display

  • SMG$CHANGE_RENDITION—Changes the video attributes of a portion of a display

  • SMG$MOVE_TEXT—Moves text from one virtual display to another

The following example uses SMG$CHANGE_VIRTUAL_DISPLAY to change the size of the WHOOPS display to five rows and seven columns and to turn off all of the display's default video attributes. If you decrease the size of a display that is on the screen, any characters in the excess area are removed from the screen.
STATUS = SMG$CHANGE_VIRTUAL_DISPLAY (WHOOPS_VDID, 
2                                    5,  ! Rows 
2                                    7,, ! Columns 
2                                    0)  ! Video attributes off
The following example uses SMG$CHANGE_RENDITION to direct attention to the first 20 columns of the statistics display by setting the reverse video attribute to the complement of the display's default setting for that attribute:
STATUS = SMG$CHANGE_RENDITION (STATS_VDID, 
2                              1,             ! Row 
2                              1,             ! Column 
2                              10,            ! Number of rows 
2                              20,            ! Number of columns 
2                              ,              ! Video-set argument 
2                              SMG$M_REVERSE) ! Video-comp argument 
2
SMG$CHANGE_RENDITION uses three sets of video attributes to determine the attributes to apply to the specified portion of the display: (1) the display's default video attributes, (2) the attributes specified by the rendition-set argument of SMG$CHANGE_RENDITION, and (3) the attributes specified by the rendition-complement argument of SMG$CHANGE_RENDITION. Table 6.2 shows the result of each possible combination.
Table 6.2. Setting Video Attributes

rendition-set

rendition-complement

Result

off

off

Uses display default

on

off

Sets attribute

off

on

Uses the complement of display default

on

on

Clears attribute

In the preceding example, the reverse video attribute is set in the rendition-complement argument but not in the rendition-set argument, thus specifying that SMG$CHANGE_RENDITION use the complement of the display's default setting to ensure that the selected portion of the display is easily seen.

Note that the resulting attributes are based on the display's default attributes, not its current attributes. If you use SMG$ routines that explicitly set video attributes, the current attributes of the display may not match its default attributes.

6.4.4.6. Using Spawned Subprocesses

You can create a spawned subprocess directly with an SMG$ routine to allow execution of a DCL command from an application. Only one spawned subprocess is allowed per virtual display. Use the following routines to work with subprocesses:
  • SMG$CREATE_SUBPROCESS—Creates a DCL spawned subprocess and associates it with a virtual display.

  • SMG$EXECUTE_COMMAND—Allows execution of a specified command in the created spawned subprocess by using mailboxes. Some restrictions apply to specifying the following commands:
    • SPAWN, GOTO, or LOGOUT cannot be used and will result in unpredictable results.

    • Single-character commands such as Ctrl/C have no effect. You can signal an end-of-file (that is, press Ctrl/Z) command by setting the flags argument.

    • A dollar sign ($) must be specified as the first character of any DCL command.

  • SMG$DELETE_SUBPROCESS—Deletes the subprocess created by SMG$CREATE_SUBPROCESS.

6.4.5. Viewports

Viewports allow you to view different pieces of a virtual display by moving a rectangular area around on the virtual display. Only one viewport is allowed for each virtual display. Once you have associated a viewport with a virtual display, the only part of the virtual display that is viewable is contained in the viewport.

The SMG$ routines for working with viewports include the following:
  • SMG$CREATE_VIEWPORT—Creates a viewport and associates it with a virtual display. You must create the virtual display first. To view the viewport, you must paste the virtual display first with SMG$PASTE_VIRTUAL_DISPLAY.

  • SMG$SCROLL_VIEWPORT—Scrolls the viewport within the virtual display. If you try to move the viewport outside of the virtual display, the viewport is truncated to stay within the virtual display. This routine allows you to specify the direction and extent of the scroll.

  • SMG$CHANGE_VIEWPORT—Moves the viewport to a new starting location and changes the size of the viewport.

  • SMG$DELETE_VIEWPORT—Deletes the viewport and dissociates it from the virtual display. The viewport is automatically unpasted. The virtual display associated with the viewport remains intact. You can unpaste a viewport without deleting it by using SMG$UNPASTE_VIRTUAL_DISPLAY.

6.4.6. Writing Text to Virtual Display

The SMG$ output routines allow you to write text to displays and to delete or modify the existing text of a display. Remember that changes to a virtual display are visible only if the virtual display is pasted to a pasteboard.

6.4.6.1. Positioning the Cursor

Each virtual display has its own logical cursor position. You can control the position of the cursor in a virtual display with the following routines:
  • SMG$HOME_CURSOR—Moves the cursor to a corner of the virtual display. The default corner is the upper left corner, that is, row 1, column 1 of the display.

  • SMG$SET_CURSOR_ABS—Moves the cursor to a specified row and column.

  • SMG$SET_CURSOR_REL—Moves the cursor to offsets from the current cursor position. A negative value means up (rows) or left (columns). A value of 0 means no movement.

In addition, many routines permit you to specify a starting location other than the current cursor position for the operation.

The SMG$RETURN_CURSOR_POS routine returns the row and column of the current cursor position within a virtual display. You do not have to write special code to track the cursor position.

Typically, the physical cursor is at the logical cursor position of the most recently written-to display. If necessary, you can use the SMG$SET_PHYSICAL_CURSOR routine to set the physical cursor location.

6.4.6.2. Writing Data Character by Character

If you are writing character by character (see Section 6.4.6.3 for line-oriented output), you can use three routines:
  • SMG$DRAW_CHAR—Puts one line-drawing character on the screen at a specified position. It does not change the cursor position.

  • SMG$PUT_CHARS—Puts one or more characters on the screen at a specified position, with the option of one video attribute.

  • SMG$PUT_CHARS_MULTI—Puts several characters on the screen at a specified position, with multiple video attributes.

These routines are simple and precise. They place exactly the specified characters on the screen, starting at a specified position in a virtual display. Anything currently in the positions written-to is overwritten; no other positions on the screen are affected. Convert numeric data to character data with language I/O statements before invoking SMG$PUT_CHARS.

The following example converts an integer to a character string and places it at a designated position in a virtual display:
CHARACTER*4 HOUSE_NO_STRING 
INTEGER*4   HOUSE_NO, 
2           LINE_NO, 
2           STATS_VDID 
   .
   .
   .
WRITE (UNIT=HOUSE_NO_STRING, 
2      FMT='(I4)') HOUSE_NO 
STATUS = SMG$PUT_CHARS (STATS_VDID, 
2                       HOUSE_NO_STRING, 
2                       LINE_NO,  ! Row 
2                       1)        ! Column

Note that the converted integer is right-justified from column 4 because the format specification is I4 and the full character string is written. To left-justify a converted number, you must locate the first nonblank character and write a substring starting with that character and ending with the last character.

Inserting and Overwriting Text

To insert characters rather than overwrite the current contents of the screen, use the routine SMG$INSERT_CHARS. Existing characters at the location written to are shifted to the right. Characters pushed out of the display are truncated; no wrapping occurs and the cursor remains at the end of the last character inserted.

Specifying Double-Size Characters

In addition to the preceding routines, you can use SMG$PUT_CHARS_WIDE to write characters to the screen in double width or SMG$PUT_CHARS_HIGHWIDE to write characters to the screen in double height and double width. When you use these routines, you must allot two spaces for each double-width character on the line and two lines for each line of double-height characters. You cannot mix single-and double-size characters on a line.

All the character routines provide rendition-set and rendition-complement arguments, which allow you to specify special video attributes for the characters being written. SMG$PUT_CHARS_MULTI allows you to specify more than one video attribute at a time. The explanation of the SMG$CHANGE_RENDITION routine in Section 6.4.4.5 discusses how to use rendition-set and rendition-complement arguments.

6.4.6.3. Writing Data Line by Line

The SMG$PUT_LINE and SMG$PUT_LINE_MULTI routines write lines to virtual displays one line after another. If the display area is full, it is scrolled. You do not have to keep track of which line you are on. All routines permit you to scroll forward (up); SMG$PUT_LINE and SMG$PUT_LINE_MULTI permit you to scroll backward (down) as well. SMG$PUT_LINE permits spacing other than single spacing.

Example 6.7 writes lines from a buffer to a display area. The output is scrolled forward if the buffer contains more lines than the display area.
Example 6.7. Scrolling Forward Through a Display
INTEGER*4     BUFF_COUNT, 
2             BUFF_SIZE (4096) 
CHARACTER*512 BUFF (4096) 
   .
   .
   .
DO I = 1, BUFF_COUNT 
  STATUS = SMG$PUT_LINE (VDID, 
2                        BUFF (I) (1:BUFF_SIZE (I))) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
END DO
Example 6.8 scrolls the output backward.
Example 6.8. Scrolling Backward Through a Display
DO I = BUFF_COUNT, 1, -1 
  STATUS = SMG$PUT_LINE (VDID, 
2                        BUFF (I) (1:BUFF_SIZE (I)), 
2                        SMG$M_DOWN) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
END DO
Cursor Movement and Scrolling

To maintain precise control over cursor movement and scrolling, you can write with SMG$PUT_CHARS and scroll explicitly with SMG$SCROLL_DISPLAY_AREA.SMG$PUT_CHARS leaves the cursor after the last character written and does not force scrolling; SMG$SCROLL_DISPLAY_AREA scrolls the current contents of the display forward, backward, or sideways without writing to the display. To restrict the scrolling region to a portion of the display area, use the SMG$SET_DISPLAY_SCROLL_REGION routine.

Inserting and Overwriting Text

To insert text rather than overwrite the current contents of the screen, use the SMG$INSERT_LINE routine. Existing lines are shifted up or down to open space for the new text. If the text is longer than a single line, you can specify whether or not you want the excess characters to be truncated or wrapped.

Using Double-Width Characters

In addition, you can use SMG$PUT_LINE_WIDE to write a line of text to the screen using double-width characters. You must allot two spaces for each double-width character on the line. You cannot mix single- and double-width characters on a line.

Specifying Special Video Attributes

All line routines provide rendition-set and rendition-complement arguments, which allow you to specify special video attributes for the text being written. SMG$PUT_LINE_MULTI allows you to specify more than one video attribute for the text. The explanation of the SMG$CHANGE_RENDITION routine in Section 6.4.4.5 discusses how to use the rendition-set and rendition-complement arguments.

6.4.6.4. Drawing Lines

The routine SMG$DRAW_LINE draws solid lines on the screen. Appropriate corner and crossing marks are drawn when lines join or intersect. The routine SMG$DRAW_CHARACTER draws a single character. You can also use the routine SMG$DRAW_RECTANGLE to draw a solid rectangle. Suppose that you want to draw an object such as that shown in Figure 6.5 in the statistics display area (an area of 10 rows by 55 columns).

Figure 6.5. Statistics Display
Statistics Display
Example 6.9 shows how you can create a statistics display using SMG$DRAW_LINE and SMG$DRAW_RECTANGLE.
Example 6.9. Creating a Statistics Display
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (10, 
2                                    55, 
2                                    STATS_VDID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Draw rectangle with upper left corner at row 1 column 1 
! and lower right corner at row 10 column 55 
STATUS =SMG$DRAW_RECTANGLE (STATS_VDID, 
2                           1, 1, 
2                           10, 55) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Draw vertical lines at columns 11, 21, and 31 
DO I = 11, 31, 10 
  STATUS = SMG$DRAW_LINE (STATS_VDID, 
2                         1, I, 
2                         10, I) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
END DO 
! Draw horizontal line at row 3 
STATUS = SMG$DRAW_LINE (STATS_VDID, 
2                       3, 1, 
2                       3, 55) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$PASTE_VIRTUAL_DISPLAY (STATS_VDID, 
2                                   PBID, 
2                                   3, 
2                                   2) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.4.6.5. Deleting Text

The following routines erase specified characters, leaving the rest of the screen intact:
  • SMG$ERASE_CHARS—Erases specified characters on one line.

  • SMG$ERASE_LINE—Erases the characters on one line starting from a specified position.

  • SMG$ERASE_DISPLAY—Erases specified characters on one or more lines.

  • SMG$ERASE_COLUMN—Erases a column from the specified row to the end of the column from the virtual display.

The following routines perform delete operations. In a delete operation, characters following the deleted characters are shifted into the empty space.
  • SMG$DELETE_CHARS—Deletes specified characters on one line. Any characters to the right of the deleted characters are shifted left.

  • SMG$DELETE_LINE—Deletes one or more full lines. Any remaining lines in the display are scrolled up to fill the empty space.

The following example erases the remaining characters on the line whose line number is specified by LINE_NO, starting at the column specified by COLUMN_NO:
STATUS = SMG$ERASE_LINE (STATS_VDID, 
2                        LINE_NO, 
2                        COLUMN_NO) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.4.7. Using Menus

You can use SMG$ routines to set up menus to read user input. The type of menus you can create include the following:
  • Block menu—Selections are in matrix format. This is the type of menu often used.

  • Vertical menu—Each selection is on its own line.

  • Horizontal menu—All selections are on one line.

Menus are associated with a virtual display, and only one menu can be used for each virtual display.

The menu routines include the following:
  • SMG$CREATE_MENU—Creates a menu associated with a virtual display. This routine allows you to specify the type of menu, the position in which the menu is displayed, the format of the menu (single or double spaced), and video attributes.

  • SMG$SELECT_FROM_MENU—Sets up menu selection capability. You can specify a default menu selection (which is shown in reverse video), whether online help is available, a maximum time limit for making a menu selection, a key indicating read termination, whether to send the text of the menu item selected to a string, and a video attribute.

  • SMG$DELETE_MENU—Discontinues access to the menu and erases it.

When you are using menus, no other output should be sent to the menu area;otherwise, unpredictable results may occur.

The default SMG$SELECT_FROM_MENU allows specific operations, such as use of the arrow keys to move up and down the menu selections, keys to make a menu selection, ability to select more than one item at a time, ability to reselect an item already selected, and the key sequence to invoke online help. By using the flags argument to modify this operation, you have the option of disallowing reselection of a menu item and of allowing any key pressed to select an item.

6.4.8. Reading Data

You can read text from a virtual display (SMG$READ_FROM_DISPLAY) or from a virtual keyboard (SMG$READ_STRING, SMG$READ_COMPOSED_LINE, or SMG$READ_KEYSTROKE). The three routines for virtual keyboard input are known as the SMG$ input routines. SMG$READ_FROM_DISPLAY is not a true input routine because it reads text from the virtual display rather than from a user.

The SMG$ input routines can be used alone or with the SMG$ output routines. This section assumes that you are using the input routines with the output routines. Section 6.5 describes how to use the input routines without the output routines.

When you use the SMG$ input routines with the SMG$ output routines, always specify the optional vdid argument of the input routine, which specifies the virtual display in which the input is to occur. The specified virtual display must be pasted to the device associated with the virtual keyboard that is specified as the first argument of the input routine. The display must be pasted in column 1, cannot be occluded, and cannot have any other display to its right; input begins at the current cursor position, but the cursor must be in column 1.

6.4.8.1. Reading from a Display

You can read the contents of the display using the routine SMG$READ_FROM_DISPLAY. By default, the read operation reads all of the characters from the current cursor position to the end of that line. The row argument of SMG$READ_FROM_DISPLAY allows you to choose the starting point of the read operation, that is, the contents of the specified row to the rightmost column in that row.

If the terminator-string argument is specified, SMG$READ_FROM_DISPLAY searches backward from the current cursor position and reads the line beginning at the first terminator encountered (or at the beginning of the line). A terminator is a character string. You must calculate the length of the character string read operation yourself.

The following example reads the current contents of the first line in the STATS_VDID display:
CHARACTER*4 STRING 
INTEGER*4   SIZE 
   .
   .
   .
STATUS = SMG$HOME_CURSOR (STATS_VDID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS)) 
STATUS = SMG$READ_FROM_DISPLAY (STATS_VDID, 
2                               STRING) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS)) 
SIZE = 55 
DO WHILE ((STRING (SIZE:SIZE) .EQ. ' ') .AND. 
2         (SIZE .GT. 1)) 
  SIZE = SIZE - 1 
END DO

6.4.8.2. Reading from a Virtual Keyboard

The SMG$CREATE_VIRTUAL_KEYBOARD routine establishes a device for input operations; the default device is the user's terminal. The routine SMG$READ_STRING reads characters typed on the screen either until the user types a terminator or until the maximum size (which defaults to 512 characters) is exceeded. (The terminator is usually a carriage return; see the routine description in the VSI OpenVMS RTL Screen Management (SMG$) Manual for a complete list of terminators). The current cursor location for the display determines where the read operation begins.

The operating system's terminal driver processes carriage returns differently than the SMG$routines. Therefore, in order to scroll input accurately, you must keep track of your vertical position in the display area. Explicitly set the cursor position and scroll the display. If a read operation takes place on a row other than the last row of the display, advance the cursor to the beginning of the next row before the next operation. If a read operation takes place on the last row of the display, scroll the display with SMG$SCROLL_DISPLAY_AREA and then set the cursor to the beginning of the row. Modify the read operation with TRM$M_TM_NOTRMECHO to ensure that no extraneous scrolling occurs.

Example 6.10 reads input until Ctrl/Z is pressed.
Example 6.10. Reading Data from a Virtual Keyboard
   .
   .
   .
! Read first record 
STATUS = SMG$HOME_CURSOR (VDID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$READ_STRING (KBID, 
2                         TEXT, 
2                         'Prompt: ', 
2                         4, 
2                         TRM$M_TM_TRMNOECHO,,, 
2                         TEXT_SIZE,, 
2                         VDID) 
! Read remaining records until CTRL/Z 
DO WHILE (STATUS .NE. SMG$_EOF) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  ! Process record 
   .
   .
   .
  ! Set up screen for next read 
  ! Display area contains four rows 
  STATUS = SMG$RETURN_CURSOR_POS (VDID, ROW, COL) 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  IF (ROW .EQ. 4) THEN 
    STATUS = SMG$SCROLL_DISPLAY_AREA (VDID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    STATUS = SMG$SET_CURSOR_ABS (VDID, 4, 1) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  ELSE 
    STATUS = SMG$SET_CURSOR_ABS (VDID,, 1) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    STATUS = SMG$SET_CURSOR_REL (VDID, 1) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  END IF 
  ! Read next record 
  STATUS = SMG$READ_STRING (KBID, 
2                           TEXT, 
2                           'Prompt: ', 
2                           4, 
2                           TRM$M_TM_TRMNOECHO,,, 
2                           TEXT_SIZE,, 
2                           VDID) 
END DO

Note

Because you are controlling the scrolling, SMG$PUT_LINE and SMG$PUT_LINE_MULTI might not scroll as expected. When scrolling a mix of input and output, you can prevent problems by using SMG$PUT_CHARS.

6.4.8.3. Reading from the Keypad

To read from the keypad in keypad mode (that is, pressing a keypad character to perform some special action rather than entering data), modify the read operation with TRM$M_TM_ESCAPE and TRM$M_TM_NOECHO. Examine the terminator to determine which key was pressed.

Example 6.11 moves the cursor on the screen in response to the user's pressing the keys surrounding the keypad 5 key. The keypad 8 key moves the cursor north (up); the keypad 9 key moves the cursor northeast; the keypad 6 key moves the cursor east (right); and so on. The SMG$SET_CURSOR_REL routine is called, instead of being invoked as a function, because you do not want to abort the program on an error. (The error attempts to move the cursor out of the display area and, if this error occurs, you do not want the cursor to move.) The read operation is also modified with TRM$M_TM_PURGE to prevent the user from getting ahead of the cursor.

See Section 6.4.8.1 for the guidelines for reading from the display.
Example 6.11. Reading Data from the Keypad
   .
   .
   .
INTEGER STATUS, 
2       PBID, 
2       ROWS, 
2       COLUMNS, 
2       VDID,     ! Virtual display ID 
2       KID,      ! Keyboard ID 
2       SMG$CREATE_PASTEBOARD, 
2       SMG$CREATE_VIRTUAL_DISPLAY, 
2       SMG$CREATE_VIRTUAL_KEYBOARD, 
2       SMG$PASTE_VIRTUAL_DISPLAY, 
2       SMG$HOME_CURSOR, 
2       SMG$SET_CURSOR_REL, 
2       SMG$READ_STRING, 
2       SMG$ERASE_PASTEBOARD, 
2       SMG$PUT_CHARS, 
2       SMG$READ_FROM_DISPLAY 
CHARACTER*31 INPUT_STRING, 
2            MENU_STRING 
INTEGER*2    TERMINATOR 
INTEGER*4    MODIFIERS 
INCLUDE '($SMGDEF)' 
INCLUDE '($TRMDEF)' 
! Set up screen and keyboard 
STATUS = SMG$CREATE_PASTEBOARD (PBID, 
2                               'SYS$OUTPUT', 
2                               ROWS, 
2                               COLUMNS) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (ROWS, 
2                                    COLUMNS, 
2                                    VDID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$PUT_CHARS (VDID, 
2                       '__ MENU CHOICE ONE', 
2                       10,30) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$PUT_CHARS (VDID, 
2                       '__ MENU CHOICE TWO', 
2                       15,30) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (KID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$PASTE_VIRTUAL_DISPLAY (VDID, 
2                                   PBID, 
2                                   1, 
2                                   1) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Put cursor in NW corner 
STATUS = SMG$HOME_CURSOR (VDID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Read character from keyboard 
MODIFIERS = TRM$M_TM_ESCAPE .OR. 
2           TRM$M_TM_NOECHO .OR. 
2           TRM$M_TM_PURGE 
STATUS = SMG$READ_STRING (KID, 
2                         INPUT_STRING, 
2                         , 
2                         6, 
2                         MODIFIERS, 
2                         , 
2                         , 
2                         , 
2                         TERMINATOR) 
DO WHILE ((STATUS) .AND. 
2         (TERMINATOR .NE. SMG$K_TRM_CR)) 
  ! Check status of last read 
  IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
  ! North 
  IF (TERMINATOR .EQ. SMG$K_TRM_KP8) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, -1, 0) 
  ! Northeast 
  ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP9) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, -1, 1) 
  ! Northwest 
  ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP7) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, -1, -1) 
  ! South 
  ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP2) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, 1, 0) 
  ! Southeast 
  ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP3) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, 1, 1) 
  ! Southwest 
  ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP1) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, 1, -1) 
  ! East 
  ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP6) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, 0, 1) 
  ! West 
  ELSE IF (TERMINATOR .EQ. SMG$K_TRM_KP4) THEN 
    CALL SMG$SET_CURSOR_REL (VDID, 0, -1) 
  END IF 
  ! Read another character 
  STATUS = SMG$READ_STRING (KID, 
2                           INPUT_STRING, 
2                           , 
2                           6, 
2                           MODIFIERS, 
2                           , 
2                           , 
2                           , 
2                           TERMINATOR) 
END DO 
! Read menu entry and process 
! 
STATUS = SMG$READ_FROM_DISPLAY (VDID, 
2                               MENU_STRING) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .
! Clear screen 
STATUS = SMG$ERASE_PASTEBOARD (PBID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
 
END

6.4.8.4. Reading Composed Input

The SMG$CREATE_KEY_TABLE routine creates a table that equates keys to character strings. When you read input using the routine SMG$READ_COMPOSED_LINE and the user presses a defined key, the corresponding character string in the table is substituted for the key. You can use the SMG$ADD_KEY_DEF routine to load the table. Composed input also permits the following:
  • If states—You can define the same key to mean different things indifferent states. You can define a key to cause a change in state. The change in state can be temporary (until after the next defined key is pressed) or permanent (until a key that changes states is pressed).

  • Input termination—You can define the key to cause termination of the input transmission (as if the Return key were pressed after the character string). If the key is not defined to cause termination of the input, the user must press a terminator or another key that does cause termination.

Example 6.12 defines keypad keys 1 through 9 and permits the user to change state temporarily by pressing the PF1 key. Pressing the keypad 1 key is equivalent to typing 1000 and pressing the Return key. Pressing PF1 key and then the keypad 1 key is equivalent to typing 10000 and pressing the Return key.
Example 6.12. Redefining Keys
INTEGER*4 TABLEID 
   .
   .
   .
! Create table for key definitions 
STATUS = SMG$CREATE_KEY_TABLE (TABLEID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Load table 
! If user presses PF1, the state changes to BYTEN 
! The BYTEN state is in effect only for the very next key 
STATUS = SMG$ADD_KEY_DEF (TABLEID, 
2                         'PF1', 
2                         ,,,'BYTEN') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Pressing KP1 through Kp9 in the null state is like typing 
! 1000 through 9000 and pressing return 
STATUS = SMG$ADD_KEY_DEF (TABLEID, 
2                         'KP1', 
2                         , 
2                         SMG$M_KEY_TERMINATE, 
2                         '1000') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$ADD_KEY_DEF (TABLEID, 
2                         'KP2', 
2                         , 
2                         SMG$M_KEY_TERMINATE, 
2                         '2000') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .
STATUS = SMG$ADD_KEY_DEF (TABLEID, 
2                         'KP9', 
2                         , 
2                         SMG$M_KEY_TERMINATE, 
2                         '9000') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Pressing KP1 through KP9 in the BYTEN state is like 
! typing 10000 through 90000 and pressing return 
STATUS = SMG$ADD_KEY_DEF (TABLEID, 
2                         'KP1', 
2                         'BYTEN', 
2                         SMG$M_KEY_TERMINATE, 
2                         '10000') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$ADD_KEY_DEF (TABLEID, 
2                         'KP2', 
2                         'BYTEN', 
2                         SMG$M_KEY_TERMINATE, 
2                         '20000') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .
STATUS = SMG$ADD_KEY_DEF (TABLEID, 
2                         'KP9', 
2                         'BYTEN', 
2                         SMG$M_KEY_TERMINATE, 
2                         '90000') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! End loading key definition table 
   .
   .
   .
! Read input which substitutes key definitions where appropriate 
STATUS = SMG$READ_COMPOSED_LINE (KBID, 
2                                TABLEID, 
2                                STRING, 
2                                SIZE, 
2                                VDID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .

Use the SMG$DELETE_KEY_DEF routine to delete a key definition; use the SMG$GET_KEY_DEF routine to examine a key definition. You can also load key definition tables with the SMG$DEFINE_KEY and SMG$LOAD_KEY_DEFS routines; use the DCL command DEFINE/KEY to specify input to these routines.

To use keypad keys 0 through 9, the keypad must be in application mode. For details, see SMG$SET_KEYPAD_MODE in the VSI OpenVMS RTL Screen Management (SMG$) Manual.

6.4.9. Controlling Screen Updates

If your program needs to make a number of changes to a virtual display, you can use SMG$ routines to make all of the changes before updating the display. The SMG$BEGIN_DISPLAY_UPDATE routine causes output operations to a pasted display to be reflected only in the display's buffers. The SMG$END_DISPLAY_UPDATE routine writes the display's buffer to the pasteboard.

The SMG$BEGIN_DISPLAY_UPDATE and SMG$END_DISPLAY_UPDATE routines increment and decrement a counter. When this counter's value is 0, output to the virtual display is sent to the pasteboard immediately. The counter mechanism allows a subroutine to request and turn off batching without disturbing the batching state of the calling program.

A second set of routines, SMG$BEGIN_PASTEBOARD_UPDATE and SMG$END_PASTEBOARD_UPDATE, allow you to buffer output to a pasteboard in asimilar manner.

6.4.10. Maintaining Modularity

When using the SMG$ routines, you must take care not to corrupt the mapping between the screen appearance and the internal representation of the screen. Therefore, observe the following guidelines:
  • Mixing SMG I/O and other forms of I/O

    In general, do not use any other form of terminal I/O while the terminal is active as a pasteboard. If you do use I/O other than SMG I/O (for example, if you invoke a subprogram that may perform non-SMG terminal I/O), first invoke the SMG$SAVE_PHYSICAL_SCREEN routine and when the non-SMG I/O completes, invoke the SMG$RESTORE_PHYSICAL_SCREEN routine, as demonstrated in the following example:
    STATUS = SMG$SAVE_PHYSICAL_SCREEN (PBID, 
    2                                  SAVE_VDID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    CALL GET_EXTRA_INFO (INFO_ARRAY) 
    STATUS = SMG$RESTORE_PHYSICAL_SCREEN (PBID, 
    2                                     SAVE_VDID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
  • Sharing the pasteboard

    A routine using the terminal screen without consideration for its current contents must use the existing pasteboard ID associated with the terminal and delete any virtual displays it creates before returning control to the high-level code. This guideline also applies to the program unit that invokes a subprogram that also performs screen I/O. The safest way to clean up your virtual displays is to call the SMG$POP_VIRTUAL_DISPLAY routine and name the first virtual display you created. The following example invokes a subprogram that uses the terminal screen:

    Invoking Program Unit

    CALL GET_EXTRA_INFO (PBID, 
    2                    INFO_ARRAY) 
       .
       .
       .
    CALL STATUS = SMG$CREATE_PASTEBOARD (PBID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

    Subprogram

    SUBROUTINE GET_EXTRA_INFO (PBID, 
    2                          INFO_ARRAY) 
       .
       .
       .
    ! Start executable code 
    STATUS = SMG$CREATE_VIRTUAL_DISPLAY (4, 
    2                                    40, 
    2                                    INSTR_VDID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    STATUS = SMG$PASTE_VIRTUAL_DISPLAY (INSTR_VDID, 
    2                                   PBID, 1, 1) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
       .
       .
       .
    STATUS = SMG$POP_VIRTUAL_DISPLAY (INSTR_VDID, 
    2                                 PBID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
     
    END
  • Sharing virtual displays

    To share a virtual display created by high-level code, the low-level code must use the virtual display ID created by the high-level code; an invoking program unit must pass the virtual display ID to the subprogram. To share a virtual display created by low-level code, the high-level code must use the virtual display ID created by the low-level code; a subprogram must return the virtual display ID to the invoking program.

    The following example permits a subprogram to use a virtual display created by the invoking program unit:

    Invoking Program Unit

    STATUS = SMG$CREATE_VIRTUAL_DISPLAY (4, 
    2                                    40, 
    2                                    INSTR_VDID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    STATUS = SMG$PASTE_VIRTUAL_DISPLAY (INSTR_VDID, 
    2                                   PBID, 1, 1) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    CALL GET_EXTRA_INFO (PBID, 
    2                    INSTR_VDID) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

    Subprogram

    SUBROUTINE GET_EXTRA_INFO (PBID, 
    2                          INSTR_VDID)

6.5. Performing Special Input/Output Actions

Screen management input routines and the SYS$QIO and SYS$QIOW system services allow you to perform I/O operations otherwise unavailable to high-level languages. For example, you can allow a user to interrupt normal program execution by typing a character and by providing a mechanism for reading that character. You can also control such things as echoing, time allowed for input, and whether data is read from the type-ahead buffer.

Some of the operations described in the following sections require the use of the SYS$QIO or SYS$QIOW system services. For more information about the QIO system services, see the VSI OpenVMS System Services Reference Manual and Chapter 7.

Other operations, described in the following sections, can be performed by calling the SMG$ input routines. The SMG$ input routines can be used alone or with the SMG$ output routines. Section 6.4 describes how to use the input routines with the output routines. This section assumes that you are using the input routines alone. To use the SMG$ input routines, do the following:
  1. Call SMG$CREATE_VIRTUAL_KEYBOARD to associate a logical keyboard with a device or file specification (SYS$INPUT by default). SMG$CREATE_VIRTUAL_KEYBOARD returns a keyboard identification number; use that number to identify the device or file to the SMG$ input routines.

  2. Call an SMG$ input routine (SMG$READ_STRING or SMG$READ_COMPOSED_LINE) to read data typed at the device associated with the virtual keyboard.

When using the SMG$ input routines without the SMG$ output routines, do not specify the optional VDID argument of the input routine.

6.5.1. Using Ctrl/C and Ctrl/Y Interrupts

The QIO system services enable you to detect a Ctrl/C or Ctrl/Y interrupt at a user terminal, even if you have not issued a read to the terminal. To do so, you must take the following steps:
  1. Queue an asynchronous system trap (AST)—Issue the SYS$QIO or SYS$QIOW system service with a function code of IO$_SETMODE modified by either IO$M_CTRLCAST (for Ctrl/C interrupts) or IO$M_CTRLYAST (for Ctrl/Y interrupts). For the P1 argument, provide the name of a subroutine to be executed when the interrupt occurs. For the P2 argument, you can optionally identify one longword argument to pass to the AST subroutine.

  2. Write an AST subroutine—Write the subroutine identified in the P1argument of the QIO system service and link the subroutine into your program. Your subroutine can take one longword dummy argument to be associated with the P2 argument in the QIO system service. You must define common areas to access any other data in your program from the AST routine.

If you press Ctrl/C or Ctrl/Y after your program queues the appropriate AST, the system interrupts your program and transfers control to your AST subroutine (this action is called delivering the AST). After your AST subroutine executes, the system returns control to your program at the point of interruption (unless your AST subroutine causes the program to exit, or unless another AST has been queued). Note the following guidelines for using Ctrl/C and Ctrl/Y ASTs:
  • ASTs are asynchronous—Since your AST subroutine does not know exactly where you are in your program when the interrupt occurs, you should avoid manipulating data or performing other mainline activities. In general, the AST subroutine should either notify the mainline code (for example, by setting a flag) that the interrupt occurred, or clean up and exit from the program (if that is what you want to do).

  • ASTs need new channels to the terminal—If you try to access the terminal with language I/O statements using SYS$INPUT or SYS$OUTPUT, you may receive a redundant I/O error. You must establish another channel to the terminal by explicitly opening the terminal.

  • Ctrl/C and Ctrl/Y ASTs are one-time ASTs—After a Ctrl/C or Ctrl/Y AST is delivered, it is dequeued. You must reissue the QIO system service if you wish to trap another interrupt.

  • Many ASTs can be queued—You can queue multiple ASTs (for the same or different AST subroutines, on the same or different channels) by issuing the appropriate number of QIO system services. The system delivers the ASTs on a last-in, first-out (LIFO) basis.

  • Unhandled Ctrl/Cs turn into Ctrl/Ys—If the user enters Ctrl/C and you do not have an AST queued to handle the interrupt, the system turns the Ctrl/C interrupt into a Ctrl/Y interrupt.

  • DCL handles Ctrl/Y interrupts—DCL handles Ctrl/Y interrupts by returning the user to DCL command level, where the user has the option of continuing or exiting from your program. DCL takes precedence over your AST subroutine for Ctrl/Y interrupts. Your Ctrl/Y AST subroutine is executed only under the following circumstances:
    • If Ctrl/Y interrupts are disabled at DCL level (SETNOCONTROL_Y) before your program is executed

    • If your program disables DCL Ctrl/Y interrupts with LIB$DISABLE_CTRL

    • If the user elects to continue your program after DCL interrupts it

  • You can dequeue Ctrl/C and Ctrl/Y ASTs—You can dequeue all Ctrl/C or Ctrl/Y ASTs on a channel by issuing the appropriate QIO system service with a value of 0 for the P1 argument (passed by immediate value). You can dequeue all Ctrl/C ASTs on a channel by issuing the SYS$CANCEL system service for the appropriate channel. You can dequeue all Ctrl/Y ASTs on a channel by issuing the SYS$DASSGN system service for the appropriate channel.

  • You can use SMG$ routines—You can connect to the terminal using the SMG$routines from either AST level or mainline code. Do not attempt to connect to the terminal from AST level if you do so in your mainline code.

Example 6.13 permits the terminal user to interrupt a display to see how many lines have been typed up to that point.
Example 6.13. Using Interrupts to Perform I/O
!Main Program 
   .
   .
   .
INTEGER STATUS 
! Accumulated data records 
CHARACTER*132 STORAGE (255) 
INTEGER*4     STORAGE_SIZE (255), 
2             STORAGE_COUNT 
! QIOW and QIO structures 
INTEGER*2 INPUT_CHAN 
INTEGER*4 CODE 
STRUCTURE /IOSTAT_BLOCK/ 
  INTEGER*2 IOSTAT 
  BYTE      TRANSMIT, 
2           RECEIVE, 
2           CRFILL, 
2           LFFILL, 
2           PARITY, 
2           ZERO 
END STRUCTURE 
RECORD /IOSTAT_BLOCK/ IOSB 
! Flag to notify program of CTRL/C interrupt 
LOGICAL*4 CTRLC_CALLED 
! AST subroutine to handle CTRL/C interrupt 
EXTERNAL CTRLC_AST 
! Subroutines 
INTEGER SYS$ASSIGN, 
2       SYS$QIOW 
! Symbols used for I/O operations 
INCLUDE '($IODEF)' 
! Put values into array 
CALL LOAD_STORAGE (STORAGE, 
2                  STORAGE_SIZE, 
2                  STORAGE_COUNT) 
! Assign channel and set up QIOW structures 
STATUS = SYS$ASSIGN ('SYS$INPUT', 
2                    INPUT_CHAN,,) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
CODE = IO$_SETMODE .OR. IO$M_CTRLCAST 
! Queue an AST to handle CTRL/C interrupt 
STATUS = SYS$QIOW (, 
2                  %VAL (INPUT_CHAN), 
2                  %VAL (CODE), 
2                  IOSB, 
2                  ,, 
2                  CTRLC_AST,    ! Name of AST routine 
2                  CTRLC_CALLED, ! Argument for AST routine 
2                  ,,,) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
IF (.NOT. IOSB.IOSTAT) 
2  CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT)) 
! Display STORAGE array, one element per line 
DO I = 1, STORAGE_COUNT 
  TYPE *, STORAGE (I) (1:STORAGE_SIZE (I)) 
 
  ! Additional actions if user types CTRL/C 
  IF (CTRLC_CALLED) THEN 
    CTRLC_CALLED = .FALSE. 
    ! Show user number of lines displayed so far 
    TYPE *, 'Number of lines: ', I 
    ! Requeue AST 
    STATUS = SYS$QIOW (, 
2                      %VAL (INPUT_CHAN), 
2                      %VAL (CODE), 
2                      IOSB, 
2                      ,, 
2                      CTRLC_AST, 
2                      CTRLC_CALLED, 
2                      ,,,) 
    IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
    IF (.NOT. IOSB.IOSTAT) 
2      CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT)) 
  END IF 
END DO 
 
END

AST Routine

! AST routine 
! Notifies program that user typed CTRL/C 
SUBROUTINE CTRLC_AST (CTRLC_CALLED) 
LOGICAL*4 CTRLC_CALLED 
CTRLC_CALLED = .TRUE. 
 
END

6.5.2. Detecting Unsolicited Input

You can detect input from the terminal even if you have not called SMG$READ_COMPOSED_LINE or SMG$READ_STRING by using SMG$ENABLE_UNSOLICITED_INPUT. This routine uses the AST mechanism to transfer control to a subprogram of your choice each time the user types at the terminal; the AST subprogram is responsible for reading any input. When the subprogram completes, control returns to the point in your mainline code where it was interrupted.

The SMG$ENABLE_UNSOLICITED_INPUT routine is not an SMG$ input routine. Before invoking SMG$ENABLE_UNSOLICITED_INPUT, you must invoke SMG$CREATE_PASTEBOARD to associate a pasteboard with the terminal and SMG$CREATE_VIRTUAL_KEYBOARD to associate a virtual keyboard with the same terminal.

SMG$ENABLE_UNSOLICITED_INPUT accepts the following arguments:
  • The pasteboard identification number (use the value returned by SMG$CREATE_PASTEBOARD)

  • The name of an AST subprogram

  • An argument to be passed to the AST subprogram

When SMG$ENABLE_UNSOLICITED_INPUT invokes the AST subprogram, it passes two arguments to the subprogram: the pasteboard identification number and the argument that you specified. Typically, you write the AST subprogram to read the unsolicited input with SMG$READ_STRING. Since SMG$READ_STRING requires that you specify the virtual keyboard at which the input was typed, specify the virtual keyboard identification number as the second argument to pass to the AST subprogram.

Example 6.14 permits the terminal user to interrupt the display of a series of arrays, and either to go on to the next array (by typing input beginning with an uppercase N) or to exit from the program (by typing input beginning with anything else).
Example 6.14. Receiving Unsolicited Input from a Virtual Keyboard
! Main Program 
! The main program calls DISPLAY_ARRAY once for each array. 
! DISPLAY_ARRAY displays the array in a DO loop. 
! If the user enters input from the terminal, the loop is 
! interrupted and the AST routine takes over. 
! If the user types anything beginning with an N, the AST 
! sets DO_NEXT and resumes execution -- DISPLAY_ARRAY drops 
! out of the loop processing the array (because DO_NEXT is 
! set -- and the main program calls DISPLAY_ARRAY for the 
! next array. 
! If the user types anything not beginning with an N, 
! the program exits. 
   .
   .
   .
INTEGER*4 STATUS, 
2         VKID,  ! Virtual keyboard ID 
2         PBID   ! Pasteboard ID 
! Storage arrays 
INTEGER*4 ARRAY1 (256), 
2         ARRAY2 (256), 
2         ARRAY3 (256) 
! System routines 
INTEGER*4 SMG$CREATE_PASTEBOARD, 
2         SMG$CREATE_VIRTUAL_KEYBOARD, 
2         SMG$ENABLE_UNSOLICITED_INPUT 
! AST routine 
EXTERNAL  AST_ROUTINE 
! Create a pasteboard 
STATUS = SMG$CREATE_PASTEBOARD (PBID,        ! Pasteboard ID 
2                               'SYS$INPUT') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Create a keyboard for the same device 
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID,  ! Keyboard ID 
2                                     'SYS$INPUT') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Enable unsolicited input 
STATUS = SMG$ENABLE_UNSOLICITED_INPUT (PBID, ! Pasteboard ID 
2                                      AST_ROUTINE, 
2                                      VKID) ! Pass keyboard 
                                             ! ID to AST 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .
! Call display subroutine once for each array 
CALL DISPLAY_ARRAY (ARRAY1) 
CALL DISPLAY_ARRAY (ARRAY2) 
CALL DISPLAY_ARRAY (ARRAY3) 
 
END

Array Display Routine

! Subroutine to display one array 
SUBROUTINE DISPLAY_ARRAY (ARRAY) 
! Dummy argument 
INTEGER*4 ARRAY (256) 
! Status 
INTEGER*4 STATUS 
! Flag for doing next array 
LOGICAL*4 DO_NEXT 
COMMON /DO_NEXT/ DO_NEXT 
! If AST has been delivered, reset 
IF (DO_NEXT) DO_NEXT = .FALSE. 
! Initialize control variable 
I = 1 
! Display entire array unless interrupted by user 
! If interrupted by user (DO_NEXT is set), drop out of loop 
DO WHILE ((I .LE. 256) .AND. (.NOT. DO_NEXT)) 
  TYPE *, ARRAY (I) 
  I = I + 1 
END DO 
 
END

AST Routine

! Subroutine to read unsolicited input 
SUBROUTINE AST_ROUTINE (PBID, 
2                       VKID) 
! dummy arguments 
INTEGER*4 PBID,                  ! Pasteboard ID 
2         VKID                   ! Keyboard ID 
! Status 
INTEGER*4 STATUS 
! Flag for doing next array 
LOGICAL*4 DO_NEXT 
COMMON /DO_NEXT/ DO_NEXT 
! Input string 
CHARACTER*4 INPUT 
! Routines 
INTEGER*4 SMG$READ_STRING 
! Read input 
STATUS = SMG$READ_STRING (VKID,  ! Keyboard ID 
2                         INPUT) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! If user types anything beginning with N, set DO_NEXT 
! otherwise, exit from program 
IF (INPUT (1:1) .EQ. 'N') THEN 
  DO_NEXT = .TRUE. 
ELSE 
  CALL EXIT 
END IF 
 
END

6.5.3. Using the Type-Ahead Buffer

Normally, if the user types at the terminal before your application is able to read from that device, the input is saved in a special data structure maintained by the system called the type-ahead buffer. When your application is ready to read from the terminal, the input is transferred from the type-ahead buffer to your input buffer. The type-ahead buffer is preset at a size of 78 bytes. If the HOSTSYNC characteristic is on (the usual condition), input to the type-ahead buffer is stopped (the keyboard locks) when the buffer is within 8 bytes of being full. If the HOSTSYNC characteristic is off, the bell rings when the type-ahead buffer is within 8 bytes of being full; if you overflow the buffer, the excess data is lost. The TTY_ALTALARM system parameter determines the point at which either input is stopped or the bell rings.

You can clear the type-ahead buffer by reading from the terminal with SMG$READ_STRING and by specifying TRM$M_TM_PURGE in the modifiers argument. Clearing the type-ahead buffer has the effect of reading only what the user types on the terminal after the read operation is invoked. Any characters in the type-ahead buffer are lost. The following example illustrates how to purge the type-ahead buffer:
INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD, 
2             SMG$READ_STRING, 
2             STATUS, 
2             VKID,      ! Virtual keyboard ID 
2             INPUT_SIZE 
CHARACTER*512 INPUT 
INCLUDE       '($TRMDEF)' 
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, 
2                                     'SYS$INPUT') ! I/O device 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$READ_STRING (VKID,     ! Keyboard ID 
2                         INPUT,    ! Data read 
2                         'Prompt> ', 
2                         512, 
2                         TRM$M_TM_PURGE, 
2                         ,, 
2                         INPUT_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

You can also clear the type-ahead buffer with a QIO read operation modified by IO$M_PURGE (defined in $IODEF). You can turn off the type-ahead buffer for further read operations with a QIO set mode operation that specifies TT$M_NOTYPEAHD as a basic terminal characteristic.

You can examine the type-ahead buffer by issuing a QIO sense mode operation modified by IO$M_TYPEAHDCNT. The number of characters in the type-ahead buffer and the value of the first character are returned to the P1 argument.

The size of the type-ahead buffer is determined by the TTY_TYPAHDSZ system parameter. You can specify an alternative type-ahead buffer by turning on the ALTYPEAHD terminal characteristic; the size of the alternative type-ahead buffer is determined by the TTY_ALTYPAHD system parameter.

6.5.4. Using Echo

Normally, the system writes back to the terminal any printable characters that the user types at that terminal. The system also writes highlighted words in response to certain control characters; for example, the system writes EXIT if the user enters Ctrl/Z. If the user types ahead of your read, the characters are not echoed until you read them from the type-ahead buffer.

You can turn off echoing when you invoke a read operation by reading from the terminal with SMG$READ_STRING and by specifying TRM$M_TM_NOECHO in the modifiers argument. You can turn off echoing for control characters only by modifying the read operation with TRM$M_TM_TRMNOECHO. The following example turns off all echoing for the read operation:
INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD, 
2             SMG$READ_STRING, 
2             STATUS, 
2             VKID,       ! Virtual keyboard ID 
2             INPUT_SIZE 
CHARACTER*512 INPUT 
INCLUDE       '($TRMDEF)' 
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID,        ! Keyboard ID 
2                                     'SYS$INPUT') ! I/O device 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$READ_STRING (VKID,        ! Keyboard ID 
2                         INPUT,       ! Data read 
2                         'Prompt> ', 
2                         512, 
2                         TRM$M_TM_NOECHO, 
2                         ,, 
2                         INPUT_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

You can also turn off echoing with a QIO read operation modified by IO$M_NOECHO (defined in $IODEF). You can turn off echoing for further read operations with a QIO set mode operation that specifies TT$M_NOECHO as a basic terminal characteristic.

6.5.5. Using Timeout

Using SMG$READ_STRING, you can restrict the user to a certain amount of time in which to respond to a read command. If your application reads data from the terminal using SMG$READ_STRING, you can modify the timeout characteristic by specifying, in the timeout argument, the number of seconds the user has to respond. If the user fails to type a character in the allotted time, the error condition SS$_TIMEOUT (defined in $SSDEF) is returned. The following example restricts the user to 8 seconds in which to respond to a read command:
INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD, 
2             SMG$READ_STRING, 
2             STATUS, 
2             VKID,     ! Virtual keyboard ID 
2             INPUT_SIZE 
CHARACTER*512 INPUT 
INCLUDE       '($SSDEF)' 
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, 
2                                     'SYS$INPUT') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$READ_STRING (VKID,      ! Keyboard ID 
2                         INPUT,     ! Data read 
2                         'Prompt> ', 
2                         512, 
2                         , 
2                         8, 
2                         , 
2                         INPUT_SIZE) 
IF (.NOT. STATUS) THEN 
  IF (STATUS .EQ. SS$_TIMEOUT) CALL NO_RESPONSE () 
ELSE 
  CALL LIB$SIGNAL (%VAL (STATUS)) 
END IF

You can cause a QIO read operation to time out after a certain number of seconds by modifying the operation with IO$M_TIMED and by specifying the number of seconds in the P3 argument. A message broadcast to a terminal resets a timer that is set for a timed read operation (regardless of whether the operation was initiated with QIO or SMG).

Note that the timed read operations work on a character-by-character basis. To set a time limit on an input record rather than an input character, you must use the SYS$SETIMR system service. The SYS$SETIMR executes an AST routine at a specified time. The specified time is the input time limit. When the specified time is reached, the AST routine cancels any outstanding I/O on the channel that is assigned to the user's terminal.

6.5.6. Converting Lowercase to Uppercase

You can automatically convert lowercase user input to uppercase by reading from the terminal with the SMG$READ_STRING routine and by specifying TRM$M_TM_CVTLOW in the modifiers argument, as shown in the following example:
INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD, 
2             SMG$READ_STRING, 
2             STATUS, 
2             VKID,      ! Virtual keyboard ID 
2             INPUT_SIZE 
CHARACTER*512 INPUT 
INCLUDE       '($TRMDEF)' 
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID,  ! Keyboard ID 
2                                     'SYS$INPUT') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$READ_STRING (VKID,    ! Keyboard ID 
2                         INPUT,   ! Data read 
2                         'Prompt> ', 
2                         512, 
2                         TRM$M_TM_CVTLOW, 
2                         ,, 
2                         INPUT_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

You can also convert lowercase characters to uppercase with a QIO read operation modified by IO$M_CVTLOW (defined in $IODEF).

6.5.7. Performing Line Editing and Control Actions

Normally, the user can edit input as explained in the VSI OpenVMS I/O User's Reference Manual. You can inhibit line editing on the read operation by reading from the terminal with SMG$READ_STRING and by specifying TRM$M_TM_NOFILTR in the modifiers argument. The following example shows how you can inhibit line editing:
INTEGER*4     SMG$CREATE_VIRTUAL_KEYBOARD, 
2             SMG$READ_STRING, 
2             STATUS, 
2             VKID,     ! Virtual keyboard ID 
2             INPUT_SIZE 
CHARACTER*512 INPUT 
INCLUDE       '($TRMDEF)' 
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID,  ! Keyboard ID 
2                                     'SYS$INPUT') 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$READ_STRING (VKID,   ! Keyboard ID 
2                         INPUT,  ! Data read 
2                         'Prompt> ', 
2                         512, 
2                         TRM$M_TM_NOFILTR, 
2                         ,, 
2                         INPUT_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

You can also inhibit line editing with a QIO read operation modified by IO$M_NOFILTR (defined in $IODEF).

6.5.8. Using Broadcasts

You can write, or broadcast, to any interactive terminal by using the SYS$BRKTHRU system service. The following example broadcasts a message to all terminals at which users are currently logged in. Use of SYS$BRKTHRU to write to a terminal allocated to a process other than your own requires the OPER privilege.
INTEGER*4 STATUS, 
2         SYS$BRKTHRUW 
INTEGER*2 B_STATUS (4) 
INCLUDE   '($BRKDEF)' 
STATUS = SYS$BRKTHRUW (, 
2                      'Accounting system started',, 
2                      %VAL (BRK$C_ALLUSERS), 
2                      B_STATUS,,,,,,) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))

6.5.8.1. Default Handling of Broadcasts

If the terminal user has taken no action to handle broadcasts, a broadcast is written to the terminal screen at the current position (after a carriage return and line feed). If a write operation is in progress, the broadcast occurs after the write ends. If a read operation is in progress, the broadcast occurs immediately; after the broadcast, any echoed user input to the aborted read operation is written to the screen (same effect as pressing Ctrl/R).

6.5.8.2. How to Create Alternate Broadcast Handlers

You can handle broadcasts to the terminal on which your program is running with SMG$SET_BROADCAST_TRAPPING. This routine uses the AST mechanism to transfer control to a subprogram of your choice each time a broadcast message is sent to the terminal; when the subprogram completes, control returns to the point in your mainline code where it was interrupted.

The SMG$SET_BROADCAST_TRAPPING routine is not an SMG$ input routine. Before invoking SMG$SET_BROADCAST_TRAPPING, you must invoke SMG$CREATE_PASTEBOARD to associate a pasteboard with the terminal. SMG$CREATE_PASTEBOARD returns a pasteboard identification number; pass that number to SMG$SET_BROADCAST_TRAPPING to identify the terminal in question. Read the contents of the broadcast with SMG$GET_BROADCAST_MESSAGE.

Example 6.15 demonstrates how you might trap a broadcast and write it at the bottom of the screen. For more information about the use of SMG$pasteboards and virtual displays, see Section 6.4.
Example 6.15. Trapping Broadcast Messages
   .
   .
   .
INTEGER*4 STATUS, 
2         PBID,                                  ! Pasteboard ID 
2         VDID,                                  ! Virtual display ID 
2         SMG$CREATE_PASTEBOARD, 
2         SMG$SET_BROADCAST_TRAPPING 
2         SMG$PASTE_VIRTUAL_DISPLAY 
COMMON    /ID/ PBID, 
2              VDID 
INTEGER*2 B_STATUS (4) 
INCLUDE   '($SMGDEF)' 
INCLUDE   '($BRKDEF)' 
EXTERNAL  BRKTHRU_ROUTINE 
STATUS = SMG$CREATE_PASTEBOARD (PBID) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$CREATE_VIRTUAL_DISPLAY (3,          ! Height 
2                                    80,         ! Width 
2                                    VDID,,      ! Display ID 
2                                    SMG$M_REVERSE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
STATUS = SMG$SET_BROADCAST_TRAPPING (PBID,    ! Pasteboard ID 
2                                       BRKTHRU_ROUTINE) ! AST 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
   .
   .
   .
SUBROUTINE BRKTHRU_ROUTINE () 
INTEGER*4 STATUS, 
2         PBID,                                  ! Pasteboard ID 
2         VDID,                                  ! Virtual display ID 
2         SMG$GET_BROADCAST_MESSAGE, 
2         SMG$PUT_CHARS, 
2         SMG$PASTE_VIRTUAL_DISPLAY 
COMMON    /ID/ PBID, 
2              VDID 
CHARACTER*240 MESSAGE 
INTEGER*2     MESSAGE_SIZE 
! Read the message 
STATUS = SMG$GET_BROADCAST_MESSAGE (PBID, 
2                                   MESSAGE, 
2                                   MESSAGE_SIZE) 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Write the message to the virtual display 
STATUS = SMG$PUT_CHARS (VDID, 
2                       MESSAGE (1:MESSAGE_SIZE), 
2                       1,                       ! Line 
2                       1)                       ! Column 
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS)) 
! Make the display visble by pasting it to the pasteboard 
STATUS = SMG$PASTE_VIRTUAL_DISPLAY (VDID, 
2                                   PBID, 
2                                   22,          ! Row 
2                                   1)           ! Column 
 
END

Chapter 7. System Service Input/Output Operations

This chapter describes how to use system services to perform input and output operations.

Examples are provided to show you how to use the I/O services for simple functions, such as terminal input and output operations. If you plan to write device-dependent I/O routines, see the VSI OpenVMS I/O User's Reference Manual.

On VAX systems, if you want to write your own device driver or connect to a device interrupt vector, see the OpenVMS VAX Device Support Reference Manual. This manual has been archived but is available on the OpenVMS Documentation CD-ROM.

Besides using I/O system services, you can use OpenVMS Record Management Services (RMS). OpenVMS RMS provides a set of routines for general-purpose, device-independent functions such as data storage, retrieval, and modification.

Unlike RMS services, I/O system services permit you to use the I/O resources of the operating system directly in a device-dependent manner. I/O services also provide some specialized functions not available in OpenVMS RMS. Using I/O services requires more programming knowledge than using OpenVMS RMS, but can result in more efficient input/output operations.

7.1. Overview of OpenVMS QIO Operations

The OpenVMS operating system provides QIO operations that perform three basic I/O functions: read, write, and set mode. The read function transfers data from a device to a user-specified buffer. The write function transfers data in the opposite direction—from a user-specified buffer to the device. For example, in a read QIO function to a terminal device, a user-specified buffer is filled with characters received from the terminal. In a write QIO function to the terminal, the data in a user-specified buffer is transferred to the terminal where it is displayed.

The set mode QIO function is used to control or describe the characteristics and operation of a device. For example, a set mode QIO function to a line printer can specify either uppercase or lowercase character format. Not all QIO functions are applicable to all types of devices. The line printer, for example, cannot perform a read QIO function.

7.2. Quotas, Privileges, and Protection

To preserve the integrity of the operating system, the I/O operations are performed under the constraints of quotas, privileges, and protection.

Quotas limit the number and type of I/O operations that a process can perform concurrently and the total size of outstanding transfers. They ensure that all users have an equitable share of system resources and usage.

Privileges are granted to a user to allow the performance of certain I/O-related operations, for example, creating a mailbox and performing logical I/O to a file-structured device. Restrictions on user privileges protect the integrity and performance of both the operating system and the services provided to other users.

Protection controls access to files and devices. Device protection is provided in much the same way as file protection: shareable and nonshareable devices are protected by protection masks.

The Set Resource Wait Mode (SYS$SETRWM) system service allows a process to select either of two modes when an attempt to exceed a quota occurs. In the enabled (default) mode, the process waits until the required resource is available before continuing. In the disabled mode, the process is notified immediately by a system service status return that an attempt to exceed a quota has occurred. Waiting for resources is transparent to the process when resource wait mode is enabled; the process takes no explicit action when a wait is necessary.

The different types of I/O-related quotas, privilege, and protection are described in the following sections.

7.2.1. Buffered I/O Quota

The buffered I/O limit quota (BIOLM) specifies the maximum number of concurrent buffered I/O operations that can be active in a process. In a buffered I/O operation, the user's data is buffered in system dynamic memory. The driver deals with the system buffer and not the user buffer. Buffered I/O is used for terminal, line printer, card reader, network, mailbox, and console medium transfers and file system operations. For a buffered I/O operation, the system does not have to lock the user's buffer in memory.

The system manager, or the person who creates the process, establishes the buffered I/O quota value in the user authorization file. If you use the Set Resource Wait Mode (SYS$SETRWM) system service to enable resource wait mode for the process, the process enters resource wait mode if it attempts to exceed its direct I/O quota.

7.2.2. Buffered I/O Byte Count Quota

The buffered I/O byte count quota (BYTLM) specifies the maximum amount of buffer space that can be consumed from system dynamic memory for buffering I/O requests. All buffered I/O requests require system dynamic memory in which the actual I/O operation takes place.

The system manager, or the person who creates the process, establishes the buffered I/O byte count quota in the user authorization file. If you use the SYS$SETRWM system service to enable resource wait mode for the process, the process enters resource wait mode if it attempts to exceed its direct I/O quota.

7.2.3. Direct I/O Quota

The direct I/O limit quota (DIOLM) specifies the maximum number of concurrent direct (unbuffered) I/O operations that a process can have active. In a direct I/O operation, data is moved directly to or from the user buffer. Direct I/O is used for disk, magnetic tape, most direct memory access (DMA) real-time devices, and nonnetwork transfers, such as DMC11/DMR11 write transfers. For direct I/O, the user's buffer must be locked in memory during the transfer.

The system manager, or the person who creates the process, establishes the direct I/O quota value in the user authorization file. If you use the SYS$SETRWM system service to enable resource wait mode for the process, the process enters resource wait mode if it attempts to exceed its direct I/O quota.

7.2.4. AST Quota

The AST quota specifies the maximum number of outstanding asynchronous system traps that a process can have. The system manager, or the person who creates the process, establishes the quota value in the user authorization file. There is never an implied wait for that resource.

7.2.5. Physical I/O Privilege

Physical I/O privilege (PHY_IO) allows a process to perform physical I/O operations on a device. Physical I/O privilege also allows a process to perform logical I/O operations on a device.

7.2.6. Logical I/O Privilege

Logical I/O privilege (LOG_IO) allows a process to perform logical I/O operations on a device. A process can also perform physical operations on a device if the process has logical I/O privilege, the volume is mounted foreign, and the volume protection mask allows access to the device. (A foreign volume is one volume that contains no standard file structure understood by any of the operating system software.) See Section 7.3.2for further information about logical I/O privilege.

7.2.7. Mount Privilege

Mount privilege (MOUNT) allows a process to use the IO$_MOUNT function to perform mount operations on disk and magnetic tape devices. The IO$_MOUNT function is used in ancillary control process (ACP) interface operations.

7.2.8. Share Privilege

Share privilege (SHARE) allows a process to use the SYS$ASSIGN system service to override another process's exclusive access request on the specified device.

Performing any I/O operations to a device driver coded to expect exclusive access – performing I/O to any device driver not explicitly coded to expect shared multiple-process access – can result in unusual and unexpected device and application behaviour, and can result in problems of device ownership, and failures during the device driver last channel deassign operation.

Using SHARE to override access is useful for a few specific situations, such as user-written device driver debugging and user-written device driver diagnostic tools. General use of SHARE is not recommended.

7.2.9. Volume Protection

Volume protection protects the integrity of mailboxes and both foreign and Files-11 On-Disk Structure Level 2 structured volumes. Volume protection for a foreign volume is established when the volume is mounted. Volume protection for a Files-11 structured volume is established when the volume is initialized. (If the process mounting the volume has the override volume protection privilege, VOLPRO, protection can be overridden when the volume is mounted).

The SYS$CREMBX system service protection mask argument establishes mailbox protection.

Set Protection QIO requests allow you to set volume protection on a mailbox. You must either be the owner of the mailbox or have the BYPASS privilege.

Protection for structured volumes and mailboxes is provided by a volume protection mask that contains four 4-bit fields. These fields correspond to the four classes of user permitted to access the volume. (User classes are based on the volume owner's UIC.)

The 4-bit fields are interpreted differently for volumes that are mounted as structured (that is, volumes serviced by an ACP), volumes that are mounted as foreign, and mailboxes (both temporary and permanent).

Figure 7.1 shows the 4-bit protection fields for mailboxes. Usually, volume protection is meaningful only for read and write operations.

Figure 7.1. Mailbox Protection Fields
Mailbox Protection Fields

7.2.10. Device Protection

Device protection protects the allocation of nonshareable devices, such as terminals and card readers.

Protection is provided by a device protection mask similar to that of volume protection. The difference is that only the bit corresponding to read access is checked, and that bit determines whether the process can allocate or assign a channel to the device.

You establish device protection with the DCL command SET PROTECTION/DEVICE. This command sets both the protection mask and the device owner UIC.

7.2.11. System Privilege

System UIC privilege (SYSPRV) allows a process to be eligible for the volume or device protection specified for the system protection class, even if the process does not have a UIC in one of the system groups.

7.2.12. Bypass Privilege

Bypass privilege (BYPASS) allows a process to bypass volume and device protection completely.

7.3. Physical, Logical, and Virtual I/O

I/O data transfers can occur in any one of three device addressing modes:physical, logical, or virtual. Any process with device access allowed by the volume protection mask can perform logical I/O on a device that is mounted foreign; physical I/O requires privileges. Virtual I/O does not require privileges; however, intervention by an ACP to control user access might be necessary if the device is under ACP control. (ACP functions are described in the VSI OpenVMS I/O User's Reference Manual).

7.3.1. Physical I/O Operations

In physical I/O operations, data is read from and written to the actual, physically addressable units accepted by the hardware (for example, sectors on a disk or binary characters on a terminal in the PASSALL mode). This mode allows direct access to all device-level I/O operations.

Physical I/O requires that one of the following conditions be met:
  • The issuing process has physical I/O privilege (PHY_IO).

  • The issuing process has all of the following characteristics:
    • The issuing process has logical I/O privilege (LOG_IO).

    • The device is mounted foreign.

    • The volume protection mask allows physical access to the device.

If neither of these conditions is met, the physical I/O operation is rejected by the SYS$QIO system service, which returns a condition value of SS$_NOPRIV (no privilege). Figure 7.2 illustrates the physical I/O access checks in greater detail.

The inhibit error-logging function modifier (IO$M_INHERLOG) can be specified for all physical I/O functions. The IO$M_INHERLOG function modifier inhibits the logging of any error that occurs during the I/O operation.

7.3.2. Logical I/O Operations

In logical I/O operations, data is read from and written to logically addressable units of the device. Logical operations can be performed on both block-addressable and record-oriented devices. For block-addressable devices (such as disks), the addressable units are 512-byte blocks. They are numbered from 0 to n -1, where n is the number of blocks on the device. For record-oriented or non-block-structured devices (such as terminals), logical addressable units are not pertinent and are ignored. Logical I/O requires that one of the following conditions be met:
  • The issuing process has physical I/O privilege (PHY_IO).

  • The issuing process has logical I/O privilege (LOG_IO).

  • The volume is mounted foreign and the volume protection mask allows access to the device.

If none of these conditions is met, the logical I/O operation is rejected by the SYS$QIO system service, which returns a condition value of SS$_NOPRIV (no privilege). Figure 7.3 illustrates the logical I/O access checks in greater detail.

7.3.3. Virtual I/O Operations

You can perform virtual I/O operations on both record-oriented (non-file-structured) and block-addressable (file-structured) devices. For record-oriented devices (such as terminals), the virtual function is the same as a logical function; the virtual addressable units of the devices are ignored.

For block-addressable devices (such as disks), data is read from and written to open files. The addressable units in the file are 512-byte blocks. They are numbered starting at 1 and are relative to a file rather than to a device. Block-addressable devices must be mounted and structured and must contain a file that was previously accessed on the I/O channel.

Virtual I/O operations also require that the volume protection mask allow access to the device (a process having either physical or logical I/O privilege can override the volume protection mask). If these conditions are not met, the virtual I/O operation is rejected by the QIO system service, which returns one of the following condition values:

Condition Value

Meaning

SS$_NOPRIV

No privilege

SS$_DEVNOTMOUNT

Device not mounted

SS$_DEVFOREIGN

Volume mounted foreign

Figure 7.4 shows the relationship of physical, logical, and virtual I/O to the driver.

Figure 7.2. Physical I/O Access Checks
Physical I/O Access Checks
Figure 7.3. Logical I/O Access Checks
Logical I/O Access Checks
Figure 7.4. Physical, Logical, and Virtual I/O
Physical, Logical, and Virtual I/O

7.4. I/O Function Encoding

I/O functions fall into three groups that correspond to the three I/O device addressing modes (physical, logical, and virtual) described in Section 7.3. Depending on the device to which it is directed, an I/O function can be expressed in one, two, or all three modes.

I/O functions are described by 16-bit, symbolically expressed values that specify the particular I/O operation to be performed and any optional function modifiers. Figure 7.5 shows the format of the 16-bit function value.

Symbolic names for I/O function codes are defined by the $IODEF macro.

Figure 7.5. I/O Function Format
I/O Function Format

7.4.1. Function Codes

The low-order 6 bits of the function value are a code that specifies the particular operation to be performed. For example, the code for read logical block is expressed as IO$_READLBLK. Table 7.1 lists the symbolic values for read and write I/O functions in the three transfer modes.
Table 7.1. Read and Write I/O Functions

Physical I/O

Logical I/O

Virtual I/O

IO$_READPBLK

IO$_READLBLK

IO$_READVBLK

IO$_WRITEPBLK

IO$_WRITELBLK

IO$_WRITEVBLK

The set mode I/O function has a symbolic value of IO$_SETMODE.

Function codes are defined for all supported devices. Although some of the function codes (for example, IO$_READVBLK and IO$_WRITEVBLK) are used with several types of devices, most are device dependent; that is, they perform functions specific to particular types of devices. For example, IO$_CREATE is a device-dependent function code; it is used only with file-structured devices such as disks and magnetic tapes. The I/O user's reference documentation provides complete descriptions of the functions and function codes.

Note

You should determine the device class before performing any QIO function, because the requested function might be incompatible with some devices. For example, the SYS$INPUT device could be a terminal, a disk, or some other device. Unless this device is a terminal, an IO$_SETMODE request that enables a Ctrl/C AST is not performed.

7.4.2. Function Modifiers

The high-order 10 bits of the function value are function modifiers. These are individual bits that alter the basic operation to be performed. For example, you can specify the function modifier IO$M_NOECHO with the function IO$_READLBLK to a terminal. When used together, the two values are written in VAX MACRO as IO$_READLBLK!IO$M_NOECHO. This causes data typed at the terminal keyboard to be entered into the user buffer but not echoed to the terminal. Figure 7.6 shows the format of function modifiers.

Figure 7.6. Function Modifier Format
Function Modifier Format

As shown in Figure 7.6, bits <15:13> are device- or function-independent bits, and bits <12:6>are device- or function-dependent bits. Device- or function-dependent bits have the same meaning, whenever possible, for different device classes. For example, the function modifier IO$M_ACCESS is used with both disk and magnetic tape devices to cause a file to be accessed during a create operation. Device- or function-dependent bits always have the same function within the same device class.

There are two device- or function-independent modifier bits: IO$M_INHRETRY and IO$M_DATACHECK (a third bit is reserved). IO$M_INHRETRY is used to inhibit all error recovery. If any error occurs and this modifier bit is specified, the operation is terminated immediately and a failure status is returned in the I/O status block (see Section 7.10). Use IO$M_DATACHECK to compare the data in memory with that on a disk or magnetic tape.

7.5. Assigning Channels

Before any input or output operation can be performed on a physical device, you must assign a channel to the device to provide a path between the process and the device. The Assign I/O Channel (SYS$ASSIGN) system service establishes this path.

When you write a call to the SYS$ASSIGN service, you must supply the name of the device, which can be a physical device name or a logical name, and the address of a word to receive the channel number. The service returns a channel number, and you use this channel number when you write an input or output request.

For example, the following lines assign an I/O channel to the device TTA2. The channel number is returned in the word at TTCHAN.
#include <descrip.h>
#include <lib$routines.h>
#include <ssdef.h>
#include <starlet.h>
#include <stdio.h>
#include <stsdef.h>

main() {
        unsigned int status;
        unsigned short ttchan;
        $DESCRIPTOR(ttname,"TTA2:");

        /* Assign a channel to a device */
        status = SYS$ASSIGN( &ttname,      /* devnam - device name  */
                             &ttchan,      /* chan - channel number */
                             0,            /* acmode - access mode  */
                             0,            /* mbxnam - logical name
                                              for mailbox           */
                             0 );          /* flags                 */
        if (!$VMS_STATUS_SUCCESS(status))
            LIB$SIGNAL(status);

 return SS$_NORMAL;
}

To assign a channel to the current default input or output device, use the logical name SYS$INPUT or SYS$OUTPUT.

For more details on how SYS$ASSIGN and other I/O services handle logical names, see Section 7.2.5.

7.5.1. Using the Share Privilege with the SYS$ASSIGN and SYS$DASSGN Services

Use of SHARE privilege should be made only with caution, as applications, application protocols, and device drivers coded to expect only exclusive access can encounter unexpected and potentially errant behavior when access to the device is unexpectedly shared via use of SHARE privilege.

If you use the SHARE privilege to override the exclusivity requested by another process's call to the system service SYS$ASSIGN, and the original process then attempts to deassign its channels via explicit calls to SYS$DASSGN or via the implicit calls to SYS$DASSGN made during image or process rundown, the OpenVMS last-channel-deassign code may not operate as expected due to the assignment of the additional I/O channels to the device. The presence of these extra channels will prevent the last-channel-deassign code from releasing the ownership of the device, potentially resulting in a device owned by the process identification (PID) of a nonexistent process.

Unless its use is explicitly supported by the application, the application protocol, and the device driver, the use of SHARE privilege is generally discouraged.

7.6. Queuing I/O Requests

All input and output operations in the operating system are initiated with the Queue I/O Request (SYS$QIO) system service. The SYS$QIO system service permits direct interaction with the system's terminal driver. SYS$QIOs permit some operations that cannot be performed with language I/O statements and RTL routines; calls to SYS$QIO reduce overhead and permit asynchronous I/O operations. However, calls to SYS$QIO are device dependent. The SYS$QIO service queues the request and returns immediately to the caller. While the operating system processes the request, the program that issued the request can continue execution.

The format for SYS$QIO is as follows:
SYS$QIO
  ([efn],chan,func[,iosb][,astadr][,astprm][,p1][,p2][,p3][,p4][,p5][,p6])

Required arguments to the SYS$QIO service include the channel number assigned to the device on which the I/O is to be performed, and a function code (expressed symbolically) that indicates the specific operation to be performed. Depending on the function code, one to six additional parameters may be required.

For example, the IO$_WRITEVBLK and IO$_READVBLK function codes are device-independent codes used to read and write single records or virtual blocks. These function codes are suitable for simple terminal I/O. They require parameters indicating the address of an input or output buffer and the buffer length. A call to SYS$QIO to write a line to a terminal may look like the following:
#include <starlet.h>

        unsigned int status, func=IO$_WRITEVBLK;
   .
   .
   .
        status = SYS$QIO(0,             /* efn - event flag       */
                        ttchan,         /* chan - channel number    */
                        func,           /* func - function modifier */
                        0,              /* iosb - I/O status block  */
                        0,              /* astadr - AST routine     */
                        0,              /* astprm - AST parameter   */
                        buffadr,        /* p1 - output buffer       */
                        buflen);        /* p2 - length of message   */

Function codes are defined for all supported device types, and most of the codes are device dependent; that is, they perform functions specific to a particular device. The $IODEF macro defines symbolic names for these function codes. For information about how to obtain a listing of these symbolic names, see Appendix A. For details about all function codes and an explanation of the parameters required by each, see the VSI OpenVMS I/O User's Reference Manual.

To read from or write to a terminal with the SYS$QIO or SYS$QIOW system service, you must first associate the terminal name with an I/O channel by calling the SYS$ASSIGN system service, then use the assigned channel in the SYS$QIO or SYS$QIOW system service. To read from SYS$INPUT or write to SYS$OUTPUT, specify the appropriate logical name as the terminal name in the SYS$ASSIGN system service. In general, use SYS$QIO for asynchronous operations, and use SYS$QIOW for all other operations.

7.7. Synchronizing Service Completion

The SYS$QIO system service returns control to the calling program as soon as a request is queued; the status code returned in R0 indicates whether the request was queued successfully. To ensure proper synchronization of the queuing operation with respect to the program, the program must do the following:
  • Test whether the operation was queued successfully.

  • Test whether the operation itself completed successfully.

Optional arguments to the SYS$QIO service provide techniques for synchronizing I/O completion. There are three methods you can use to test for the completion of an I/O request:
  • Specify the number of an event flag to be set when the operation completes.

  • Specify the address of an AST routine to be executed when the operation completes.

  • Specify the address of an I/O status block in which the system can place the return status when the operation completes.

    I/O status blocks are explained in Section 7.10.

The use of these three techniques is shown in the examples that follow. Example 7.1 shows specifying event flags.
Example 7.1. Event Flags
#include <lib$routines.h>
#include <starlet.h>
unsigned int status, efn=0, efn1=1, efn=2;
   .
   .
   .
status = SYS$QIO(efn1,...);   /* Issue 1st I/O request */
if (!$VMS_STATUS_SUCCESS(status))
LIB$SIGNAL( status );                 /* Queued successfully? */  1
   .
   .
   .
status = SYS$QIO(efn2,...);   /* Issue second I/O request */      2
if (!$VMS_STATUS_SUCCESS(status))     /* Queued successfully? */
        LIB$SIGNAL( status );
   .
   .
   .3
status = SYS$WFLAND( efn,             /* Wait until both are done */
                     &mask,...4
                     .
                     .
                     .

1

When you specify an event flag number as an argument, SYS$QIO clears the event flag when it queues the I/O request. When the I/O completes, the flag is set.

2

In this example, the program issues two Queue I/O requests. A different event flag is specified for each request.

3

The Wait for Logical AND of Event Flags (SYS$WFLAND) system service places the process in a wait state until both I/O operations are complete. The efn argument indicates that the event flags are both in cluster 0; the mask argument indicates the flags for which the process is to wait.

4

Note that the SYS$WFLAND system service (and the other wait system services) wait for the event flag to be set; they do not wait for the I/O operation to complete. If some other event were to set the required event flags, the wait for event flag would complete too soon. You must coordinate the use of event flags carefully. (See Section 7.8 for a discussion of the recommended method for testing I/O completion).

Example 7.2 shows specifying an AST routine.
Example 7.2. AST Routine
#include <lib$routines.h>
#include <starlet.h>
#include <stsdef.h>
        unsigned int status, astprm=1;
   .
   .
   .
        status = SYS$QIO(...&ttast,   /* I/O request with AST */       1
                        astprm...);
        if (!$VMS_STATUS_SUCCESS( status ))   /* Queued successfully? */
                LIB$SIGNAL( status );
   .
   .
   .
}

void ttast ( int astprm ) {                   /* AST service routine */ 2

/* Handle I/O completion */
   .
   .
   .

        return;
}                              /* End of AST routine */

1

When you specify the astadr argument to the SYS$QIO system service, the system interrupts the process when the I/O completes and passes control to the specified AST service routine.

The SYS$QIO system service call specifies the address of the AST routine, TTAST, and a parameter to pass as an argument to the AST service routine. When$QIO returns control, the process continues execution.

2

When the I/O completes, the AST routine TTAST is called, and it responds to the I/O completion. By examining the AST parameter, TTAST can determine the origin of the I/O request.

When this routine is finished executing, control returns to the process at the point at which it was interrupted. If you specify the astadr argument in your call to SYS$QIO, you should also specify the iosb argument so that the AST routine can evaluate whether the I/O completed successfully.

Example 7.3 shows specifying an I/O status block.
Example 7.3. I/O Status Block
#include <lib$routines.h>
#include <stdio.h>
#include <ssdef.h>
#include <starlet.h>
#include <stsdef.h>

   .
   .
   .
/* I/O  status block */
        struct {
                 unsigned short iostat, iolen;
                 unsigned int dev_info;
}ttiosb;                                               1

        unsigned int status;
   .
   .
   .
        status = SYS$QIO(,..., &ttiosb, ...);          2
        if( !$VMS_STATUS_SUCCESS( status )) /* Queued successfully? */
                LIB$SIGNAL( status );
   .
   .
   .
        while(ttiosb.iostat == 0) {
        /* Loop -- with delay -- until done */         3

        }

        if( !$VMS_STATUS_SUCCESS( ttiosb.iostat )) {
        /* Perform error handling */
   .
   .
   .
        }

1

An I/O status block is a quadword structure that the system uses to post the status of an I/O operation. You must define the quadword area in your program. TTIOSB defines the I/O status block for this I/O operation. The iosb argument in the SYS$QIO system service refers to this quadword.

2

Instead of polling the low-order word of the I/O status block for the completion status, the program uses the preferred method of using an event flag and calling SYS$SYNCH to determine I/O completion.

3

The process polls the I/O status block. If the low-order word still contains zero, the I/O operation has not yet completed. In this example, the program loops until the request is complete.

7.8. Recommended Method for Testing Asynchronous Completion

VSI recommends that you use the Synchronize (SYS$SYNCH) system service to wait for completion of an asynchronous event. The SYS$SYNCH service correctly waits for the actual completion of an asynchronous event, even if some other event sets the event flag.

To use the SYS$SYNCH service to wait for the completion of an asynchronous event, you must specify both an event flag number and the address of an I/O status block (IOSB) in your call to the asynchronous system service. The asynchronous service queues the request and returns control to your program. When the asynchronous service completes, it sets the event flag and places the final status of the request in the IOSB.

In your call to SYS$SYNCH, you must specify the same efn and I/O status block that you specified in your call to the asynchronous service. The SYS$SYNCH service waits for the event flag to be set by means of the SYS$WAITFR system service. When the specified event flag is set, SYS$SYNCH checks the specified I/O status block. If the I/O status block is nonzero, the system service has completed and SYS$SYNCH returns control to your program. If the I/O status block is zero, SYS$SYNCH clears the event flag by means of the SYS$CLREF service and calls the $WAITFR service to wait for the event flag to be set.

The SYS$SYNCH service sets the event flag before returning control to your program. This ensures that the call to SYS$SYNCH does not interfere with testing for completion of another asynchronous event that completes at approximately the same time and uses the same event flag to signal completion.

The following call to the Queue I/O Request (SYS$QIO) system service demonstrates how the SYS$SYNCH service is used:
   .
   .
   .
#include <lib$routines.h>
#include <starlet.h>
        unsigned int status, event_flag = 1;
        struct {
                        short int iostat, iolen;
                        unsigned int dev_info;
}ttiosb;
   .
   .
   .
/* Request I/O */
        status = SYS$QIO (event_flag, ..., &ttiosb ...);
        if (!$VMS_STATUS_SUCCESS(status))
                LIB$SIGNAL( status );
   .
   .
   .
/* Wait until I/O completes */
        status = SYS$SYNCH (event_flag, &ttiosb );
        if (!$VMS_STATUS_SUCCESS(status))
                LIB$SIGNAL( status );
   .
   .
   .

Note

The SYS$QIOW service provides a combination of SYS$QIO and SYS$SYNCH.

7.9. Synchronous and Asynchronous Forms of Input/Output Services

You can execute some input/output services either synchronously or asynchronously. A W at the end of a system service name indicates the synchronous version of the system service.

The synchronous version of a system service combines the functions of the asynchronous version of the service and the Synchronize (SYS$SYNCH) system service. The synchronous version acts exactly as if you had used the asynchronous version of the system service followed immediately by a call to SYS$SYNCH; it queues the I/O request, and then places the program in a wait state until the I/O request completes. The synchronous version takes the same arguments as the asynchronous version.

Table 7.2 lists the asynchronous and synchronous names of input/output services that have synchronous versions.
Table 7.2. Asynchronous Input/Output Services and Their Synchronous Versions

Asynchronous Name

Synchronous Name

Description

$BRKTHRU

$BRKTHRUW

Breakthrough

$GETDVI

$GETDVIW

Get Device/Volume Information

$GETJPI

$GETJPIW

Get Job/Process Information

$GETLKI

$GETLKIW

Get Lock Information

$GETQUI

$GETQUIW

Get Queue Information

$GETSYI

$GETSYIW

Get Systemwide Information

$QIO

$QIOW

Queue I/O Request

$SNDJBC

$SNDJBCW

Send to Job Controller

$UPDSEC

$UPDSECW

Update Section File on Disk

7.9.1. Reading Operations with SYS$QIOW

The SYS$QIO and SYS$QIOW system services move one record of data from a terminal to a variable. For synchronous I/O, use SYS$QIOW. Complete information about the SYS$QIO and SYS$QIOW system services is presented in the VSI OpenVMS System Services Reference Manual.

The SYS$QIO and SYS$QIOW system services place the data read in the variable specified in the 1 argument. The second word of the status block contains the offset from the beginning of the buffer to the terminator—hence, it equals the size of the data read. Always reference the data as a substring, using the offset to the terminator as the position of the last character (that is, the size of the substring). If you reference the entire buffer, your data will include the terminator for the operation (for example, the CR character) and any excess characters from a previous operation using the buffer. (The only exception to the substring guideline is if you deliberately overflow the buffer to terminate the I/O operation).

Example 7.4 shows use of the SYS$QIOW system service and reads a line of data from the terminal and waits for the I/O to complete.
Example 7.4. Reading Data from the Terminal Synchronously
   .
   .
   .
INTEGER STATUS
! QIOW structures
INTEGER*2 INPUT_CHAN             ! I/O channel
INTEGER CODE,                    ! Type of I/O operation
2       INPUT_BUFF_SIZE,         ! Size of input buffer
2       PROMPT_SIZE,             ! Size of prompt
2       INPUT_SIZE               ! Size of input line as read
PARAMETER (PROMPT_SIZE = 13,
2          INPUT_BUFF_SIZE = 132)
CHARACTER*132 INPUT
CHARACTER*(*) PROMPT
PARAMETER (PROMPT = 'Input value: ')
! Define symbols used in I/O operations
INCLUDE '($IODEF)'
! Status block for QIOW
STRUCTURE /IOSTAT_BLOCK/
  INTEGER*2 IOSTAT,              ! Return status
2           TERM_OFFSET,         ! Location of line terminator
2           TERMINATOR,          ! Value of terminator
2           TERM_SIZE            ! Size of terminator
END STRUCTURE
RECORD /IOSTAT_BLOCK/ IOSB
! Subprograms
INTEGER*4 SYS$ASSIGN,
2         SYS$QIOW
   .
   .
   .
! Assign an I/O channel to SYS$INPUT
STATUS = SYS$ASSIGN ('SYS$INPUT',
2                    INPUT_CHAN,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Read with prompt
CODE = IO$_READPROMPT
STATUS = SYS$QIOW (,
2                  %VAL (INPUT_CHAN),
2                  %VAL (CODE),
2                  IOSB,
2                  ,,
2                  %REF (INPUT),
2                  %VAL (INPUT_BUFF_SIZE),
2                  ,,
2                  %REF (PROMPT),
2                  %VAL (PROMPT_SIZE))
! Check QIOW status
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Check status of I/O operation
IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT))
! Set size of input string
INPUT_SIZE = IOSB.TERM_OFFSET
   .
   .
   .

7.9.2. Reading Operations with SYS$QIO

To perform an asynchronous read operation, use the SYS$QIO system service and specify an event flag (the first argument, which must be passed by value).Your program continues while the I/O is taking place. When you need the input from the I/O operation, invoke the SYS$SYNCH system service to wait for the event flag and status block specified in the SYS$QIO system service. If the I/O is not complete, your program pauses until it is. In this manner, you can overlap processing within your program. Naturally, you must take care not to assume data has been returned by the I/O operation before you call SYS$SYNCH and it returns successfully. Example 7.5 demonstrates an asynchronous read operation.
Example 7.5. Reading Data from the Terminal Asynchronously
   .
   .
   .
INTEGER STATUS
! QIO structures
INTEGER*2 INPUT_CHAN     ! I/O channel
INTEGER CODE,            ! Type of I/O operation
2       INPUT_BUFF_SIZE, ! Size of input buffer
2       PROMPT_SIZE,     ! Size of prompt
2       INPUT_SIZE       ! Size of input line as read
PARAMETER (INPUT_BUFF_SIZE = 132,
2          PROMPT = 13)
CHARACTER*132 INPUT
CHARACTER*(*) PROMPT
PARAMETER (PROMPT = 'Input value: ')
INCLUDE '($IODEF)'        ! Symbols used in I/O operations
! Status block for QIO
STRUCTURE /IOSTAT_BLOCK/
  INTEGER*2 IOSTAT,       ! Return status
2           TERM_OFFSET,  ! Location of line terminator
2           TERMINATOR,   ! Value of terminator
2           TERM_SIZE     ! Size of terminator
END STRUCTURE
RECORD /IOSTAT_BLOCK/ IOSB
! Event flag for I/O
INTEGER INPUT_EF
! Subprograms
INTEGER*4 SYS$ASSIGN,
2         SYS$QIO,
2         SYS$SYNCH,
2         LIB$GET_EF
   .
   .
   .
! Assign an I/O channel to SYS$INPUT
STATUS = SYS$ASSIGN ('SYS$INPUT',
2                    INPUT_CHAN,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Get an event flag
STATUS = LIB$GET_EF (INPUT_EF)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Read with prompt
CODE = IO$_READPROMPT
STATUS = SYS$QIO (%VAL (INPUT_EF),
2                 %VAL (INPUT_CHAN),
2                 %VAL (CODE),
2                 IOSB,
2                 ,,
2                 %REF (INPUT),
2                 %VAL (INPUT_BUFF_SIZE),
2                 ,,
2                 %REF (PROMPT),
2                 %VAL (PROMPT_SIZE))
! Check status of QIO
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
   .
   .
   .
STATUS = SYS$SYNCH (%VAL (INPUT_EF),
2                   IOSB)
! Check status of SYNCH
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Check status of I/O operation
IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT))
! Set size of input string
INPUT_SIZE = IOSB.TERM_OFFSET
   .
   .
   .

Be sure to check the status of the I/O operation as returned in the I/O status block. In an asynchronous operation, you can check this status only after the I/O operation is complete (that is, after the call to SYS$SYNCH).

7.9.3. Write Operations with SYS$QIOW

The SYS$QIO and SYS$QIOW system services move one record of data from a character value to the terminal. Do not use these system services, as described here, for output to a file or nonterminal device.

For synchronous I/O, use SYS$QIOW and omit the first argument (the event flag number). For complete information about SYS$QIO and SYS$QIOW, refer to the VSI OpenVMS System Services Reference Manual.

Example 7.6 writes a line of character data to the terminal.
Example 7.6. Writing Character Data to a Terminal
INTEGER STATUS,
2       ANSWER_SIZE
CHARACTER*31 ANSWER
INTEGER*2 OUT_CHAN
! Status block for QIO
STRUCTURE /IOSTAT_BLOCK/
  INTEGER*2 IOSTAT,
2           BYTE_COUNT,
2           LINES_OUTPUT
  BYTE      COLUMN,
2           LINE
END STRUCTURE
RECORD /IOSTAT_BLOCK/ IOSB
! Routines
INTEGER SYS$ASSIGN,
2       SYS$QIOW
! IO$ symbol definitions
INCLUDE '($IODEF)'
   .
   .
   .
STATUS = SYS$ASSIGN ('SYS$OUTPUT',
2                    OUT_CHAN,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
STATUS = SYS$QIOW (,
2                  %VAL (OUT_CHAN),
2                  %VAL (IO$_WRITEVBLK),
2                  IOSB,
2                  ,
2                  ,
2                  %REF ('Answer: '//ANSWER(1:ANSWER_SIZE)),
2                  %VAL (8+ANSWER_SIZE),
2                  ,
2                  %VAL (32),,) ! Single spacing
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
IF (.NOT. IOSB.IOSTAT) CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT))
END

7.10. I/O Completion Status

When an I/O operation completes, the system posts the completion status in the I/O status block, if one is specified. The completion status indicates whether the operation completed successfully, the number of bytes that were transferred, and additional device-dependent return information.

Figure 7.7 illustrates the format for the SYS$QIO system service of the information written in the IOSB.

Figure 7.7. I/O Status Block
I/O Status Block

The first word contains a system status code indicating the success or failure of the operation. The status codes used are the same as for all returns from system services; for example, SS$_NORMAL indicates successful completion.

The second word contains the number of bytes actually transferred in the I/O operation. Note that for some devices this word contains only the low-order word of the count. For information about specific devices, see the VSI OpenVMS I/O User's Reference Manual.

The second longword contains device-dependent return information.

System services other than SYS$QIO use the quadword I/O status block, but the format is different. See the description of each system service in the VSI OpenVMS System Services Reference Manual for the format of the information written in the IOSB for that service.

To ensure successful I/O completion and the integrity of data transfers, you should check the IOSB following I/O requests, particularly for device-dependent I/O functions. For complete details about how to use the I/O status block, see the VSI OpenVMS I/O User's Reference Manual.

7.11. Deassigning I/O Channels

When a process no longer needs access to an I/O device, it should release the channel assigned to the device by calling the Deassign I/O Channel (SYS$DASSGN) system service:
$DASSGN_S CHAN=TTCHAN

This service call releases the terminal channel assignment acquired in the SYS$ASSIGN example shown in Section 7.5. The system automatically deassigns channels for a process when the image that assigned the channel exits.

7.12. Using Complete Terminal I/O

The following example shows a complete sequence of input and output operations using the $QIOW macro to read and write lines to the current default SYS$INPUT device. Because the input/output of this program must be to the current terminal, it functions correctly only if you execute it interactively.
#include <descrip.h>
#include <iodef.h>
#include <lib$routines.h>
#include <ssdef.h>
#include <starlet.h>
#include <stdio.h>
#include <string.h>
#define BUFSIZ 80
/* I/O status block */
struct {                                                             1
        unsigned short iostat, ttiolen;
        unsigned int dev_info;
}ttiosb;

main() {
        unsigned int status ,outlen, inlen = BUFSIZ;
        unsigned short ttchan;
        char buffer[BUFSIZ];                                         2
        $DESCRIPTOR(ttname,"SYS$INPUT");                             3

/* Assign a channel */
        status = SYS$ASSIGN(&ttname,    /* devnam - device number */ 4
                        &ttchan,        /* chan - channel number */
                        0, 0, 0);
        if (!$VMS_STATUS_SUCCESS(status))
                LIB$SIGNAL( status );

/* Request I/O */
        status = SYS$QIOW(0,                    /* efn - event flag */
                        ttchan,                 /* chan - channel number */
                        IO$_READVBLK,           /* func - function modifier */
                        &ttiosb,                /* iosb - I/O status block */
                        0,                      /* astadr - AST routine */
                        0,                      /* astprm - AST parameter */
                        buffer,                 /* p1 - buffer */
                        inlen,                  /* p2 - length of buffer */
                        0, 0, 0, 0);        5
        if (!$VMS_STATUS_SUCCESS( status )) 6
                LIB$SIGNAL( status );

/* Get length from IOSB */
        outlen = ttiosb.ttiolen;        7

status = SYS$QIOW(0, ttchan, IO$_WRITEVBLK, &ttiosb, 0, 0, buffer, outlen,
                0, 0, 0, 0);
        if (!$VMS_STATUS_SUCCESS( status ))
                LIB$SIGNAL( status );   8

/* Deassign the channel */
        status = SYS$DASSGN( ttchan ); /* chan - channel */  9
        if (!$VMS_STATUS_SUCCESS( status ))
                LIB$SIGNAL( status );

}

1

The IOSB for the I/O operations is structured so that the program can easily check for the completion status (in the first word) and the length of the input string returned (in the second word).

2

The string will be read into the buffer BUFFER; the longword OUTLEN will contain the length of the string for the output operation.

3

The TTNAME label is a character string descriptor for the logical device SYS$INPUT, and TTCHAN is a word to receive the channel number assigned to it.

4

The $ASSIGN service assigns a channel and writes the channel number at TTCHAN.

5

If the $ASSIGN service completes successfully, the $QIOW macro reads a line from the terminal, and requests that the completion status be posted in the I/O status block defined at TTIOSB.

6

The process waits until the I/O is complete, then checks the first word in the I/O status block for a successful return. If unsuccessful, the program takes an error path.

7

The length of the string read is moved into the longword at OUTLEN, because the $QIOW macro requires a longword argument. However, the length field of the I/O status block is only 1 word long. The $QIOW macro writes the line just read to the terminal.

8

The program performs error checks. First, it ensures that the $OUTPUT macro successfully queued the I/O request; then, when the request is completed, it ensures that the I/O was successful.

9

When all I/O operations on the channel are finished, the channel is deassigned.

7.13. Canceling I/O Requests

If a process must cancel I/O requests that have been queued but not yet completed, it can issue the Cancel I/O On Channel (SYS$CANCEL) system service. All pending I/O requests issued by the process on that channel are canceled; you cannot specify a particular I/O request.

The SYS$CANCEL system service performs an asynchronous cancel operation. This means that the application must wait for each I/O operation issued to the driver to complete before checking the status for that operation.

For example, you can call the SYS$CANCEL system service as follows:
        unsigned int status, efn1=3, efn2=4;
   .
   .
   .
        status = SYS$QIO(efn1, ttchan, &iosb1, ...);
        status = SYS$QIO(efn2, ttchan, &iosb2, ...);
   .
   .
   .
        status = SYS$CANCEL(ttchan);
        status = SYS$SYNCH(efn1, &iosb1);
        status = SYS$SYNCH(efn2, &iosb2);

In this example, the SYS$CANCEL system service initiates the cancellation of all pending I/O requests to the channel whose number is located at TTCHAN.

The SYS$CANCEL system service returns after initiating the cancellation of the I/O requests. If the call to SYS$QIO specified either an event flag, AST service routine, or I/O status block, the system sets either the flag, delivers the AST, or posts the I/O status block as appropriate when the cancellation is completed.

7.14. Logical Names and Physical Device Names

When you specify a device name as input to an I/O system service, it can be a physical device name or a logical name. If the device name contains a colon (:), the colon and the characters after it are ignored. When an underscore character (_) precedes a device name string, it indicates that the string is a physical device name string, for example, _TTB3:.

Any string that does not begin with an underscore is considered a logical name, even though it may be a physical device name. Table 7.3 lists system services that translate a logical name iteratively until a physical device name is returned, or until the system default number of translations have been performed.
Table 7.3. System Services for Translating Logical Names

System Service

Definition

SYS$ALLOC

Allocate Device

SYS$ASSIGN

Assign I/O Channel

SYS$BRDCST

Broadcast

SYS$DALLOC

Deallocate Device

SYS$DISMOU

Dismount Volume

SYS$GETDEV

Get I/O Device Information

SYS$GETDVI

Get Device/Volume Information

SYS$MOUNT

Mount Volume

In each translation, the logical name tables defined by the logical name LNM$FILE_DEV are searched in order. These tables, listed in search order, are normally LNM$PROCESS, LNM$JOB, LNM$GROUP, and LNM$SYSTEM. If a physical device name is located, the I/O request is performed for that device.

If the services do not locate an entry for the logical name, the I/O service treats the name specified as a physical device name. When you specify the name of an actual physical device in a call to one of these services, include the underscore character to bypass the logical name translation.

When the SYS$ALLOC system service returns the device name of the physical device that has been allocated, the device name string returned is prefixed with an underscore character. When this name is used for the subsequent SYS$ASSIGN system service, the SYS$ASSIGN service does not attempt to translate the device name.

If you use logical names in I/O service calls, you must be sure to establish a valid device name equivalence before program execution. You can do this either by issuing a DEFINE command from the command stream, or by having the program establish the equivalence name before the I/O service call with the Create Logical Name (SYS$CRELNM) system service.

For details about how to create and use logical names, see Chapter 18.

7.15. Device Name Defaults

If, after logical name translation, a device name string in an I/O system service call does not fully specify the device name (that is, device, controller, and unit), the service either provides default values for nonspecified fields, or provides values based on device availability.

The following rules apply:
  • The SYS$ASSIGN and SYS$DALLOC system services apply default values, as shown in Table 7.4.

  • The SYS$ALLOC system service treats the device name as a generic device name and attempts to find a device that satisfies the components of the device name specified, as shown in Table 7.4.


Table 7.4. Default Device Names for I/O Services

Device

Device Name?

Generic Device

Key
  • dd—Specified device type (capital letters indicate a specific controller; numbers indicate a specific unit)
  • c—Specified controller
  • x—Any controller
  • u—Specified unit number
  • y—Any unit number

dd:

ddA0: (unit 0 on controller A)

ddxy: (any available device of the specified type)

ddc:

ddc0: (unit 0 on controller specified)

ddcy: (any available unit on the specified controller)

ddu:

ddA u: (unit specified on controller A)

ddxu: (device of specified type and unit on any available controller)

ddcu:

ddcu: (unit and controller specified)

ddcu: (unit and controller specified)

7.16. Obtaining Information About Physical Devices

The Get Device/Volume Information (SYS$GETDVI) system service returns information about devices. The information returned is specified by an item list created before the call to SYS$GETDVI.

When you call the SYS$GETDVI system service, you must provide the address of an item list that specifies the information to be returned. The format of the item list is described in the description of SYS$GETDVI in the VSI OpenVMS System Services Reference Manual. The VSI OpenVMS I/O User's Reference Manual contains details on the device-specific information these services return.

In cases where a generic (that is, nonspecific) device name is used in an I/O service, a program may need to find out what device has been used. To do this, the program should provide SYS$GETDVI with the number of the channel to the device and request the name of the device with the DVI$_DEVNAM item identifier.

The operating system also supports a device called the null device for program development. The mnemonic for the null device is NL. Its characteristics are as follows:
  • A read from NL returns an end-of-file error (SS$_ENDOFFILE).

  • A write to NL immediately returns a success message (SS$_NORMAL