HP 3000 Manuals

Switching to CM [ Switch Programming User's Guide ] MPE/iX 5.0 Documentation


Switch Programming User's Guide

Switching to CM 

To illustrate a mixed-mode switch to Compatibility Mode (CM), consider
the example of an SPL procedure DECMADD that adds two packed decimal
numbers, OPERAND1 and OPERAND2, and leaves a packed decimal result in
RESULT. DECMADD also has a parameter DIGITS that contains the total
number of whole digits in each parameter and a parameter FRAC that
contains the total number of fractional digits in each parameter.

The SPL declaration portion of the DECMADD procedure follows:

          +---------+
     +----| MPE V/E |---------------------------------------+
     |    +---------+                                       |
     |                                                      |
     |  PROCEDURE DECMADD(OPERAND1, OPERAND2, RESULT,       |
     |                         DIGITS, FRAC);               |
     |            VALUE        DIGITS, FRAC;                |
     |            BYTE ARRAY   OPERAND1, OPERAND2, RESULT;  |
     |            INTEGER      DIGITS, FRAC;                |
     |                                                      |
     |                                                      |
     +------------------------------------------------------+

OPERAND1, OPERAND2, DIGITS, and FRAC are input parameters, while RESULT
is an output parameter.

DIGITS and FRAC are declared as value parameters.  OPERAND1, OPERAND2,
and RESULT, on the other hand, are reference parameters.

To access the DECMADD procedure from Native Mode (NM) code, you need to
write a Switch stub and place that stub procedure in an Executable
Library.  This stub must call the appropriate Switch intrinsic
(HPSWITCHTOCM), which, in turn, calls DECMADD.

An example call to this procedure would be as follows:

        DECMADD(x,y,z,3,2)

where x = 100.01 and y = 156.86.  Upon return from the procedure, z =
256.87.

Writing the DECMADD Stub Declarations 

Begin the process of writing a Switch stub with the declaration portion
of the procedure.  To guarantee that the switching process is transparent
to the calling program, follow these guidelines:

 *  Make the stub name identical to that of the SPL procedure.

 *  Make the types of the stub parameters correspond to those of the SPL
    procedure.

Type correspondence refers both to status as either value or reference
parameter, as well as to the data type of the parameter.  In SPL, value
parameters are explicitly declared.  In HP Pascal/XL, it is reference
parameters that are explicitly declared by being preceded by the
designation VAR. Data type correspondence is not quite so easily
obtained.  To match the SPL INTEGER and BYTE ARRAY data types in HP
Pascal/XL, you need to use declared types.

The declaration portion includes the following:

 *  Stub header

 *  Stub parameters

 *  Called external procedures

Declaring the Stub Header.  The following extract is a possible
declaration portion for the stub procedure to enable a call to the SPL
DECMADD procedure:

          +--------+
     +----| MPE XL |-------------------------------------------+
     |    +--------+                                           |
     |                                                         |
     | TYPE                                                    |
     |    decm_number : PACKED ARRAY [1..80] OF CHAR;          |
     |                                                         |
     | PROCEDURE DECMADD(VAR OPERAND1, OPERAND2 : decm_number; |
     |                   VAR RESULT : decm_number;             |
     |                   DIGITS, FRAC  :  shortint);           |
     |                                                         |
     +---------------------------------------------------------+


NOTE The data type shortint is used to correspond to the SPL INTEGER type because DECMADD is a CM routine and integers in Compatibility Mode are 16-bit.
Declaring the Stub Parameters. The next step in writing a Switch stub is to set up the parameters required by the particular Switch intrinsic, in this instance HPSWITCHTOCM. Consider again the declaration of the HPSWITCHTOCM intrinsic: PROCEDURE HPSWITCHTOCM; INTRINSIC; Next comes an example of an HP Pascal/XL call to HPSWITCHTOCM: HPSWITCHTOCM(proc, method, numparms, parms, funcreturnlen, funcvalue, conditioncode, userstatus); You call the HPSWITCHTOCM intrinsic with eight parameters. These parameters provide Switch with the following information: * Name and CM library or the plabel of the target procedure. * Whether the target procedure runs in normal, split-stack, or no-copy mode. * Number of parameters being passed to the target procedure. * A list containing a description of each parameter being passed, including: * Pointer to each parameter; that is, a reference to where the parameter begins in NM memory (value parameters larger than one byte must be 16-bit aligned). * Length (size) of each parameter in bytes (must be positive integer <= 2 ** 16). * For reference parameters, the stub must specify whether it is a byte or word address and also indicate whether the parameter is an input and/or output parameter. * Length of the function return value (0 if not a function). * Pointer to the function return value (nil if not a function). * CM condition code value for the target procedure. * Status record to report on HPSWITCHTOCM's operation. Setting up these parameters involves three stages: 1. Declaring the necessary constants 2. Declaring the necessary user-defined types 3. Declaring the necessary variables Declaring Constants. Constants are declared to correspond to the following possibilities: * How does the operating system find the target procedure? * By number * By name * By plabel * Which CM library is the target procedure in? * System SL * Logon public SL * Logon group SL * Program's public SL * Program's group SL * Is the target procedure called in split-stack mode? * Of what type is a parameter? * Value parameter * Reference parameter requiring a word address * Reference parameter requiring a byte address * What are the valid condition and status codes? The excerpt in Example 5-1 is a typical constant declaration section for a Switch stub procedure: Example 5-1. DECMADD Stub, Constant Declarations const {The OS finds procedure either by number, by name, or } {by plabel. } pidt_known = 0; {it is found by number } pidt_load = 1; {it must be found by name} pidt_plabel = 2; {it is found by plabel } {Which library is the procedure in?} system_sl = 0; logon_pub_sl = 1; logon_group_sl = 2; pub_sl = 3; group_sl = 4; {Is the procedure split-stack callable?} method_normal = 0; {non-split-stack } method_split = 1; {split-stack callable} method_no_copy =2; {no-copy method } {What is the parameter type?} parm_type_value = 0; {value parameter } parm_type_word_ref = 1; {word address is required} parm_type_byte_ref = 2; {byte address is required} {Condition and status code constants} ccg = 0; {condition code greater (>)} ccl = 1; {condition code less (<) } cce = 2; {condition code equal (=) } All_Ok = 0; {Used in status check } {end Example 5-1} Declaring User-Defined Types. Several user-declared types are necessary to set up the stub procedure variables. These include the following: * Type to represent parameter names (a packed array of characters) * Variant type to represent the HPSWITCHTOCM proc parms parameter (contents depend on whether the target procedure is called by number, by name, or by plabel) * Array of records type to represent the HPSWITCHTOCM parms parameter, where the individual records are declared to be a packed record type having the following components: * Pointer to where the parameter is located * Length value indicating the size of the parameter * Value indicating parameter type (value, word-address reference, or byte-address reference) * For reference parameters, a value indicating status as either an input, output, or input/output parameter * Condition and status types The type declaration section for the DECMADD stub procedure follows in Example 5-2: Example 5-2. DECMADD Stub, Type Declarations type bit8 = 0..255; bit16 = 0..65535; bit8_a1 = $ALIGNMENT 1$ bit8; bit16_a1 = $ALIGNMENT 1$ bit16; {type declaration for procedure names} cm_proc_name = packed array [1..16] of char; {defining generic buffer type} generic_buffer = packed array [1..65535] of char; {defining type of HPSWITCHTOCM proc parameter} scm_procedure = packed record {variant record} case p_proc_id_type : bit8 of {proc found by number} pidt_known : ( p_fill : bit8_a1; p_proc_id : bit16_a1 ); {proc found by name} pidt_load : ( p_lib : bit8_a1; p_proc_name : cm_proc_name); {proc found by plabel} pidt_plabel : ( p_plabel : bit16_a1); end; {record} {defining type of indicator as input and/or output } {parameter } scm_io_type = set of ( INPUT_PARM, OUTPUT_PARM ); {define individual record of HPSWITCHTOCM parms parameter;} {parms is array describing the stub parameters; } {each record describes a parameter } parm_desc = packed record pd_parmptr : globalanyptr; {where parameter found} pd_parmlen : bit16; {size in bytes } pd_parm_type : bit16; {byte or word address } pd_io_type : scm_io_type; {input and/or output } end; {defining type of HPSWITCHTOCM parms parameter} scm_parm_desc_array = array [0..31] of parm_desc; Example 5-2. DECMADD Stub, Type Declarations, Continued {defining condition code type} ccode_type = shortint; {defining status code type} xlstatus = record case integer of 0 : (all : integer); 1 : (info : shortint; subsys : shortint); end; {record} {end Example 5-2} Declaring Variables. You declare two groups of variables in the variable declaration section of the stub: * Eight variables required by the HPSWITCHTOCM intrinsic * Any local variables The variable declaration section for the DECMADD stub follows in Example 5-3: Example 5-3. DECMADD Stub, Variable Declarations var proc : scm_procedure; {target procedure name} parms : scm_parm_desc_array; {describes target } {proc's parameters} method : integer; {split-stack callable?} nparms : integer; {# of target's parameters} {declaring return parameters} funclen : integer; {length of return value } funcptr : integer; {pointer to return value } status : xlstatus; {how MPE XL returns warnings} cond_code : ccode_type; {how condition code returned} {declaring local variables} loc_digits : shortint; loc_frac : shortint; {end Example 5-3} Declaring Called External Procedures. To conclude the global declarations, you declare the external procedures called within the Switch stub procedure (see Example 5-4). Example 5-4. DECMADD Stub, External Procedure Declarations {declaring the HPSWITCHTOCM intrinsic} PROCEDURE HPSWITCHTOCM; INTRINSIC; {declaring the HPSETCCODE intrinsic} PROCEDURE HPSETCCODE; INTRINSIC; {declaring the QUIT intrinsic} PROCEDURE QUIT; INTRINSIC; {end Example 5-4} Writing the DECMADD Stub Body Once the declaration section is complete, it is possible to proceed to the body of the Switch stub procedure. In the body, the actual work of setting up the parameters required by the HPSWITCHTOCM intrinsic is done. Specifically, the body does the following: * Initializes local variables * Initializes the Switch intrinsic parameter variables * Describes each target procedure parameter by doing the following: * Giving a pointer to the parameter's location * Giving the length (size) of the parameter * Indicating whether it is a value or a reference parameter * Indicating whether it is an input or an output parameter (if a reference parameter) * Calls the HPSWITCHTOCM intrinsic to change modes * Sets the condition code upon return from Switch * Tests the MPE XL status value * Takes action on errors The body of the DECMADD stub procedure follows in Example 5-5. It is intended as a lab exercise to test your understanding of the operation of Switch stubs. Use the accompanying code documentation to assist you in completing the lines of code. You can check the correctness of your choices by turning to Example 5-6 immediately following this exercise where the DECMADD stub is presented in its entirety. Example 5-5. DECMADD Stub, Stub Body begin {initializing local variables} loc_digits :=-----------; {initialize local copy of DIGITS} loc_frac :=-----------; {initialize local copy of FRAC } {initializing SWITCH variables} proc.p_proc_id_type :=--------------; {find procedure} {by name } proc.p_lib := -------------; {look in PUB SL (LIB=P)} proc.p_proc_name := '----------------'; {procedure name} method := -----------------; {NOT split-stack} {callable } nparms := ------; {number of parameters} funclen := ------; {length, in bytes, of function} {return } funcptr := ------; {pointer to function return} {value } {In the following, "describe" involves the following:} { 1) give a pointer to the parameter's location } { 2) give the length (size) of the parameter } { 3) indicate whether value or reference parameter } { IF a reference parameter, THEN } { 4) indicate whether input or output parameter } {describe OPERAND1 -- input by reference} {determine pointer to parameter's location; } {addr function takes parameter as argument and returns} {address } parms[0].pd_parmptr := addr(-----------------); {determine length of parameter; sizeof function takes } {parameter as argument and returns number of bytes } parms[0].pd_parmlen := sizeof(--------------------); {reference parameter requiring byte address} parms[0].pd_parm_type := -----------------------------; {input parameter} parms[0].pd_io_type := [----------------------]; Example 5-5. DECMADD Stub, Stub Body, Continued {describe OPERAND2 -- input by reference} {determine pointer to parameter's location; } {addr function takes parameter as argument and returns} {address } parms[1].pd_parmptr := addr(-----------------); {determine length of parameter; sizeof function takes } {parameter as argument and returns number of bytes } parms[1].pd_parmlen := sizeof(-------------------); reference parameter requiring byte address} parms[1].pd_parm_type := -----------------------------; {input parameter} parms[1].pd_io_type := [----------------------]; {describe RESULT -- output by reference} {determine pointer to parameter's location; } {addr function takes parameter as argument and returns} {address } parms[2].pd_parmptr := addr(-----------------); {determine length of parameter; sizeof function takes } {parameter as argument and returns number of bytes } parms[2].pd_parmlen := sizeof(-------------------); {reference parameter requiring byte address} parms[2].pd_parm_type := -----------------------------; {output parameter} parms[2].pd_io_type := [----------------------]; {describe DIGITS -- input by value} {determine pointer to parameter's location; } {addr function takes parameter as argument and returns} {address } parms[3].pd_parmptr := addr(-----------------); Example 5-5. DECMADD Stub, Stub Body, Continued {determine length of parameter; sizeof function takes} {parameter as argument and returns number of bytes } parms[3].pd_parmlen := sizeof(-----------------------); {value parameter} parms[3].pd_parm_type := -----------------------------; {input parameter} parms[3].pd_io_type := [----------------------]; {describe FRAC -- input by value} {determine pointer to parameter's location; } {addr function takes parameter as argument and returns} {address } parms[4].pd_parmptr := addr(-----------------); {determine length of parameter; sizeof function takes} {parameter as argument and returns number of bytes } parms[4].pd_parmlen := sizeof(-------------------); {value parameter} parms[4].pd_parm_type := -----------------------------; {input parameter} parms[4].pd_io_type := [----------------------]; {call the Switch intrinsic to change modes} HPSWITCHTOCM(--------, --------, --------, --------, --------, --------, --------, --------); {set condition code upon return from Switch} HPSETCCODE(--------); {test MPE XL status value and set ccode if not OK} if (status.all <> all_ok) then begin QUIT(status.info); end; end; {stub procedure DECMADD} {end Example 5-5} Finished DECMADD Stub Putting all these pieces together yields the following stub procedure declaration (see Example 5-6): Example 5-6. Finished DECMADD (NM--> CM) Stub $os 'mpe/xl'$ $subprogram$ $standard_level 'ext_modcal'$ $tables off$ $code_offsets off$ $xref off$ $type_coercion 'representation'$ PROGRAM Xampl56(input,output); TYPE decm_number = PACKED ARRAY [1..80] of CHAR; PROCEDURE DECMADD(var OPERAND1, OPERAND2 : decm_number; var RESULT : decm_number; DIGITS, FRAC : shortint); (* SPL calling sequence: variable name type value PROCEDURE decmadd( OPERAND1 BYTE ARRAY REFERENCE OPERAND2 BYTE ARRAY REFERENCE RESULT BYTE ARRAY REFERENCE DIGITS INTEGER VALUE FRAC) INTEGER VALUE *) const pidt_known = 0; {it's found by number} pidt_load = 1; {it's found by name } pidt_plabel = 2; {it's found by plabel} system_sl = 0; logon_pub_sl = 1; logon_group_sl = 2; pub_sl = 3; group_sl = 4; method_normal = 0; {not callable from split stack} method_split = 1; {callable in split-stack mode} method_no_copy =2; {callable in no-copy-mode} parm_type_value = 0; {value parameter } parm_type_word_ref = 1; {word address is required} parm_type_byte_ref = 2; {byte address is required} Example 5-6. Finished DECMADD (NM--> CM) Stub, Continued ccg = 0; {condition code greater (>)} ccl = 1; {condition code less (<) } cce = 2; {condition code equal (=) } All_Ok = 0; {used in status check } type bit8 = 0..255; bit16 = 0..65535; bit8_a1 = $ALIGNMENT 1$ bit8; bit16_a1 = $ALIGNMENT 1$ bit16; cm_proc_name = packed array [1..16] of char; generic_buffer = packed array [1..65535] of char; scm_procedure = packed record case p_proc_id_type : bit8 of pidt_known : ( p_fill : bit8_a1; p_proc_id : bit16_a1 ); pidt_load : ( p_lib : bit8_a1; p_proc_name : cm_proc_name ); pidt_plabel : ( p_plabel : bit16_a1); end; scm_io_type = set of ( INPUT_PARM, OUTPUT_PARM ); parm_desc = packed record pd_parmptr : globalanyptr; pd_parmlen : bit16; pd_parm_type : bit16; pd_io_type : scm_io_type; end; scm_parm_desc_array = array [0..31] of parm_desc; ccode_type = shortint; xlstatus = record case integer of 0 : (all : integer); 1 : (info : shortint; subsys : shortint); end; {record} Example 5-6. Finished DECMADD (NM--> CM) Stub, Continued PROCEDURE HPSWITCHTOCM; INTRINSIC; PROCEDURE HPSETCCODE; INTRINSIC; PROCEDURE QUIT; INTRINSIC; {End of OUTER BLOCK GLOBAL declarations} var proc : scm_procedure; parms : scm_parm_desc_array; method : integer; {split-stack callable?} nparms : integer; {# of parameters} funclen : integer; funcptr : integer; status : xlstatus; cond_code : ccode_type; loc_digits : shortint; loc_frac : shortint; begin { Initialize local variables } loc_digits := DIGITS; loc_frac := FRAC; { Initialize Switch variables } proc.p_proc_id_type := pidt_load; {find procedure by name} proc.p_lib := pub_sl; {look in PUB SL (LIB=P)} proc.p_proc_name := 'DECMADD'; {procedure name} method := method_normal; {not split-stack callable} nparms := 5; {number of parameters} funclen := 0; {length, in bytes, of function return} funcptr := 0; {pointer to function return value} {OPERAND1 -- input by reference} parms[0].pd_parmptr := addr(OPERAND1); parms[0].pd_parmlen := sizeof(OPERAND1); parms[0].pd_parm_type := parm_type_byte_ref; parms[0].pd_io_type := [input_parm]; {OPERAND2 -- input by reference} parms[1].pd_parmptr := addr(OPERAND2); parms[1].pd_parmlen := sizeof(OPERAND2); parms[1].pd_parm_type := parm_type_byte_ref; parms[1].pd_io_type := [input_parm]; Example 5-6. Finished DECMADD (NM--> CM) Stub, Continued {RESULT -- output by reference} parms[2].pd_parmptr := addr(RESULT); parms[2].pd_parmlen := sizeof(RESULT); parms[2].pd_parm_type := parm_type_byte_ref; parms[2].pd_io_type := [output_parm]; {DIGITS -- input by value} parms[3].pd_parmptr := addr(loc_digits); parms[3].pd_parmlen := sizeof(DIGITS); parms[3].pd_parm_type := parm_type_value; parms[3].pd_io_type := [input_parm]; {FRAC -- input by value} parms[4].pd_parmptr := addr(loc_frac); parms[4].pd_parmlen := sizeof(FRAC); parms[4].pd_parm_type := parm_type_value; parms[4].pd_io_type := [input_parm]; {Execute the SWITCH} HPSWITCHTOCM(proc, method, nparms, parms, funclen, funcptr, cond_code, status); HPSETCCODE(cond_code); if (status.all <> all_ok) then begin QUIT(status.info); end; end; {procedure DECMADD} BEGIN {Program Outer Block Code} END. {Program Outer Block Code} {end Example 5-6} Checklist for Writing NM--> CM Switch Stubs The following list summarizes the steps involved in writing your own NM--> CM Switch stub procedure: * Make the stub name identical to that of the target procedure. * Make the names of the stub parameters identical to those of the target procedure (observing HP Pascal/XL naming conventions and restrictions). * Make the types of the stub parameters correspond to those of the target procedure. * Set up the parameters required by the HPSWITCHTOCM intrinsic. * Declare the necessary constants * Declare the necessary user-defined types * Declare the necessary variables * Conclude the global declarations by declaring the procedures called within the Switch stub procedure (HPSWITCHTOCM, HPSETCCODE, and QUIT) * Initialize local variables * Initialize the Switch intrinsic parameter variables * Describe each target procedure parameter by doing the following: * Give a pointer to the parameter's location * Give the length (size) of the parameter * Indicate whether it is a value or a reference parameter * Indicate whether it is an input or an output parameter (if a reference parameter) * Call the HPSWITCHTOCM intrinsic to change modes * Set the condition code upon return from Switch * Test the MPE XL status value * Take action upon errors returned Switching to NM To illustrate a mixed-mode Switch from Compatibility Mode (CM) to Native Mode (NM), consider the example of a Pascal/V procedure that calls an intrinsic that is available only in Native Mode. It makes this call in order to access a new NM capability. The HPCICOMMAND intrinsic executes an MPE XL command programmatically. Unlike the MPE V/E-compatible COMMAND intrinsic, HPCICOMMAND allows you to execute UDC's and command files programmatically. The syntax of the HPCICOMMAND intrinsic is as follows: CA I16 I16 I16V HPCICOMMAND(cmdimage,errnum,parmnum,msglevel); The cmdimage parameter passes a required character array that represents a command string in the current language of the system. The string is limited to 280 bytes and must be terminated by a carriage return or be blank-filled up to the 280th byte. The errnum parameter is a required 16-bit signed integer that is passed by reference and returns the Command Interpreter error number. If there are no errors, the value returned is 0. The parmnum parameter is a required 16-bit integer that is passed by reference and indicates the nature of the error (if errnum <> 0). If parmnum is positive, this is a file system error. If parmnum is negative, then the absolute value of parmnum specifies the column number where the error occurred. The msglevel parameter is a required 16-bit integer that is passed by value and indicates how error and warning messages are handled. The following list summarizes the valid values of the msglevel parameter and the meanings of those values: 0 | All errors/warnings are printed to $STDLIST. 1 | All Command Interpreter error/warnings are printed to | $STDLIST. 2 | No errors/warnings are printed. This is the default. For more information on the HPCICOMMAND intrinsic, refer to the appropriate entry in the MPE XL Intrinsics Reference Manual (32650-90028). To access the HPCICOMMAND intrinsic from CM code, you need to write a CM--> NM Switch stub and place that stub procedure in a CM Segmented Library (SL). This stub must call the appropriate Switch intrinsic (either HPSWTONMNAME or HPSWTONMPLABEL), which, in turn, invokes HPCICOMMAND. HPSWTONMNAME calls a procedure by name, while HPSWTONMPLABEL allows you to call an NM procedure by plabel. The examples in the following paragraphs illustrate a call by plabel. Writing the CMCICommand Stub Declarations Begin the process of writing a Switch stub with the declaration portion of the procedure. The declaration portion includes the following: * Stub header * Stub parameters * Called external procedures Declaring the Stub Header. The following excerpt is a possible header declaration for the stub procedure to enable a call to the Native Mode HPCICOMMAND intrinsic: +---------------------------------------------------------+ | | | TYPE | | pac280 = PACKED ARRAY [1..280] OF CHAR; | | shortint = -32768..32767; | | | | PROCEDURE CMCICommand(VAR CICmdName : pac280; | | VAR CIErrNum : shortint; | | VAR CIParmNum : shortint; | | CIMsgLevel : shortint); | | | +---------------------------------------------------------+ Declaring the Stub Parameters. The next step in writing a CM--> NM Switch stub is to set up the parameters required by the particular Switch intrinsic(s), in this instance HPLOADNMPROC and HPSWTONMPLABEL. Consider again the Pascal/V declaration of these intrinsics: FUNCTION HPSWTONMPLABEL : integer; intrinsic; FUNCTION HPLOADNMPROC : integer; intrinsic; Next come examples of Pascal/V calls to these intrinsics: plabel := HPLOADNMPROC (procname, procnamelen, libname, libnamelen); returnstatus := HPSWTONMPLABEL (plabel, numparms, arglistarray, argdescarray, functype); You call the HPLOADNMPROC intrinsic with four parameters. These parameters provide Switch with the following information: * Name of the NM target procedure * Length of the name of the NM target procedure * Name of the library to be searched for the NM target procedure * Length of the name of the library to be searched The HPSWTONMPLABEL intrinsic is called with five parameters, providing Switch with this information: * Plabel of the NM routine (as returned by the HPLOADNMPROC intrinsic) * Number of parameters in the NM routine * Parameter list (value parameters represented by their values; reference parameters, by their DB-relative addresses) * Parameter description list (specifying the type of the parameters) * Type of the functional return value (if any) Setting up these parameters involves two stages: 1. Declaring the necessary user-defined types 2. Declaring the necessary variables Declaring User-Defined Types. Several user-declared types are necessary to set up the stub procedure variables. These include the following: * String type to represent procedure and library names * String type to represent MPE XL command or UDC names * Status Type * Integer subrange type to represent error numbers, the number of parameters, message levels, the length of character strings, and function types * Array of integer subrange type values to represent and describe the parameters of the target procedure The type declaration section for the CMCICommand stub procedure follows in Example 5-7: Example 5-7. CMCICommand Stub, Type Declarations {Type Declarations} TYPE shortint = -32768..32767; shr_ary32 = packed array [1..32] of shortint; pac16 = packed array [1..16] of char; pac280 = packed array [1..280] of char; {end Example 5-7} Declaring Variables. You declare two groups of variables in the variable declaration section of the stub: * Nine variables required by the HPLOADNMPROC and HPSWTONMPLABEL intrinsics * Any local variables The variable declaration section for the CMCICommand stub procedure follows in Example 5-8: Example 5-8. CMCICommand Stub, Variable Declarations {Global variable declarations} VAR {Parameters passed to HPCICOMMAND via Switch} CmdName : pac280; ErrNum : shortint; ParmNum : shortint; MsgLevel : shortint; {Stub procedure variable declarations} VAR {HPLOADNMPROC intrinsic parameters} proc_name : pac16; {name of NM routine} proc_len : shortint; {length of target's name} lib_nam : pac16; {NM library to search for target} lib_len : shortint; {length of NM library name} {HPSWTONMPLABEL intrinsic parameters} proc : integer; {plabel of NM routine} nparms : shortint; {number of target parameters} arglist : shr_ary32; {parameter list} argdesc : shr_ary32; {parameter description list} fct_typ : shortint; {functional return type, if any} {Assigned functional return} rtn_st : integer; {end Example 5-8} Declaring Called External Procedures. You must also declare the procedures called within the Switch stub procedure (see Example 5-9). Example 5-9. CMCICommand Stub, External Procedure Declarations {Intrinsic procedure declarations} FUNCTION HPSWTONMNAME : integer; intrinsic; FUNCTION HPSWTONMPLABEL : integer; intrinsic; FUNCTION HPLOADNMPROC : integer; intrinsic; {end Example 5-9} Writing the CMCICommand Stub Body Once the declaration section is complete, proceed to the body of the Switch stub procedure. In the body, the actual work of setting up the parameters required by the HPLOADNMPROC and HPSWTONMPLABEL intrinsics is done. Specifically, the body does the following: * Initializes local variables * Initializes the Switch intrinsic parameter variables as follows: * Designates names as string values * Designates the lengths of the strings * Designates the number of target procedures * Designates the type of the target's functional return * Initializes arrays to represent and describe target procedure parameters * Calls the HPLOADNMPROC and HPSWTONMPLABEL intrinsics * Copies local variables back to formal parameters * Tests the status value * Takes action on errors The body of the CMCICommand stub procedure follows in Example 5-10. It is intended as a lab exercise to test your understanding of the operation of Switch stubs. Use the accompanying code documentation to assist you in completing the lines of code. You can check the correctness of your choices by turning to Example 5-11 immediately following this exercise where the CMCICommand stub is presented in its entirety. Example 5-10. CMCICommand Stub, Stub Body BEGIN {Stub procedure CMCICommand} {Initializations} proc_name := ' ----------------' ; {name of target} proc_len := ----; {length of target name} lib_name := ' ----------------' ; {name of library} lib_len := ----; {length of library name} nparms := ---; {nparms governs how many types are} {searched for in argdesc; } {4 parms plus extensible_gateway } {HPCICOMMAND is declared } {$OPTION ' EXTENSIBLE_GATEWAY$ } arglist[1] := --; {extensible gateway mask is 32 bits} arglist[2] := --; {of anything, all 0 bits works ok! } arglist[3] := baddress(------------); {reference parameter passed by address; } {take byte address of variable name buffer} arglist[4] := waddress(-----------); {reference parameter passed by address;} {take word address of error number parm} arglist[5] := waddress(------------); {reference parameter passed by address;} {take word address of parameter number } {parm } arglist[6] := -------------; {value parameter represented by its value} argdesc[1] := ---; {32-bit word value for gateway mask} argdesc[2] := ---; {byte pointer for arglist[3]} argdesc[3] := ---; {word pointer for arglist[4]} argdesc[4] := ---; {word pointer for arglist[5]} argdesc[5] := ---; {shortint value parameter} fct_typ := ---; {This is an NM procedure, not a } {function. If it was an NM function, the } {return value would come back in } {arglist[1..n] where n is the length of } {the value in 16-bit words. } Example 5-10. CMCICommand Stub, Stub Body, Continued {HPLOADNMPROC intrinsic call -- 4 parameters} proc := HPLOADNMPROC (------------, {target name} ------------, {length of target } {name } ------------, {library name} ------------); {length of library} {name } {HPSWTONMPLABEL intrinsic call -- 5 parameters} rtn_st := HPSWTONMPLABEL (------------, {returned plabel} ------------, {number of target} {parameters } ------------, {target argument } {list array } ------------, {target argument } {description array} ------------); {type of target} {functional return} END; {Stub procedure CMCICommand} {end Example 5-10} Finished CMCICommand Stub Putting all these pieces together yields the following stub procedure declaration (see Example 5-11): Example 5-11. Finished CMCICommand (CM--> NM) Stub { XAMPL511 -- example Switch to NM by plabel } $standard_level " HP3000" $ {$subprogram$} {uncomment this to make an RBM for your SL} $uslinit$ PROGRAM XAMPL511(input, output); {Type Declarations} TYPE shortint = -32768..32767; shr_ary32 = packed array [1..32] of shortint; pac16 = packed array [1..16] of char; pac280 = packed array [1..280] of char; {Global variable declarations} VAR {Parameters passed to HPCICOMMAND via Switch} CmdName : pac280; ErrNum : shortint; ParmNum : shortint; MsgLevel : shortint; {Intrinsic procedure declarations} FUNCTION HPSWTONMNAME : integer; intrinsic; FUNCTION HPSWTONMPLABEL : integer; intrinsic; FUNCTION HPLOADNMPROC : integer; intrinsic; Example 5-11. Finished CMCICommand (CM--> NM) Stub, Continued {Stub procedure declaration} PROCEDURE CMCICommand(VAR CICmdName : pac280; VAR CIErrNum : shortint; VAR CIParmNum : shortint; CIMsgLevel : shortint); VAR {HPLOADNMPROC intrinsic parameters} proc_name : pac16; {name of NM routine} proc_len : shortint; {length of target's name} lib_name : pac16; {NM library to search for target} lib_len : shortint; {length of NM library name} {HPSWTONMPLABEL intrinsic parameters} proc : integer; {plabel of NM routine} nparms : shortint; {number of target parameters} arglist : shr_ary32; {parameter list} argdesc : shr_ary32; {parameter description list} fct_typ : shortint; {functional return type, if any} {Assigned functional return} rtn_st : integer; BEGIN {Stub procedure CMCICommand} {Initializations} proc_name := ' HPCICOMMAND ' ; proc_len := 11; lib_name := ' NL.PUB.SYS ' ; lib_len := 10; nparms := 5; {nparms governs how many types are} {searched for in argdesc; } {4 parms plus extensible_gateway } {HPCICOMMAND is declared } {$OPTION 'EXTENSIBLE_GATEWAY$ } Example 5-11. Finished CMCICommand (CM--> NM) Stub, Continued arglist[1] := 0; {extensible gateway mask is 32 bits} arglist[2] := 0; {of anything, all 0 bits works ok! } arglist[3] := baddress(CICmdName); {reference parameter passed by address; } {take byte address of variable name buffer} arglist[4] := waddress(CIErrNum); {reference parameter passed by address;} {take word address of error number parm} arglist[5] := waddress(CIParmNum); {reference parameter passed by address;} {take word address of parameter number } {parm } arglist[6] := CIMsgLevel; {value parameter represented by its value} argdesc[1] := 03; {32-bit word value for gateway mask} argdesc[2] := 05; {byte pointer for arglist[3]} argdesc[3] := 06; {word pointer for arglist[4]} argdesc[4] := 06; {word pointer for arglist[5]} argdesc[5] := 02; {shortint value parameter} fct_typ := 00; {This is an NM procedure, not a } {function. If it was an NM function, the } {return value would come back in } {arglist[1..n] where n is the length of } {the value in 16-bit words. } {HPLOADNMPROC intrinsic call} proc := HPLOADNMPROC (proc_name, proc_len, lib_name, lib_len); {HPSWTONMPLABEL intrinsic call} rtn_st := HPSWTONMPLABEL (proc, nparms, arglist, argdesc, fct_typ); END; {Stub procedure CMCICommand} Example 5-11. Finished CMCICommand (CM--> NM) Stub, Continued BEGIN {outer block} . . . CmdName := ' ' ; ErrNum := 0; ParmNum := 0; MsgLevel := 0; CMCICommand(CmdName, ErrNum, ParmNum, MsgLevel); . . . END. {outer block} {end Example 5-11} Checklist for Writing CM--> NM Switch Stubs The following list summarizes the steps involved in writing your own CM--> NM Switch stub procedure: * Set up the parameters required by the HPSWTONMNAME or HPLOADNMPROC and HPSWTONMPLABEL intrinsics * Declare the necessary constants * Declare the necessary user-defined types * Declare the necessary variables * Conclude the global declarations by declaring the procedures called within the Switch stub procedure * Initialize local variables * Initialize the Switch intrinsic parameter variables * Call the HPSWTONMNAME intrinsic or the HPLOADNMPROC and HPSWTONMPLABEL intrinsics to change modes * Test the status value * Take action upon errors returned


MPE/iX 5.0 Documentation