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