Examples: NM to CM and Return [ Switch Programming User's Guide ] MPE/iX 5.0 Documentation
Switch Programming User's Guide
Examples: NM to CM and Return
Now consider an example of the NM--> CM mixed-mode switching process.
Suppose there is an SPL procedure CONVERTDATE that resides in a CM SL and
that you want to access from MPE XL code. CONVERTDATE converts an input
parameter FROMDATE to an output parameter TODATE, based on the values of
the FROMFORMAT and TOFORMAT parameters.
The SPL declaration of the procedure is as follows:
+---------------------------------------------------------+
| |
|PROCEDURE CONVERTDATE (FROMDATE, TODATE, |
| FROMFORMAT, TOFORMAT, DATELENGTH);|
| VALUE FROMFORMAT, TOFORMAT, DATELENGTH; |
| BYTE ARRAY FROMDATE, TODATE; |
| INTEGER FROMFORMAT, TOFORMAT, DATELENGTH; |
| |
+---------------------------------------------------------+
The DATELENGTH parameter contains the number of bytes in the date.
The FROMFORMAT, TOFORMAT, and DATELENGTH parameters are declared to be
value parameters, while FROMDATE and TODATE are passed by reference.
Because the CONVERTDATE procedure resides in a CM SL, you can write a
Switch stub that calls the HPSWITCHTOCM intrinsic, which, in turn, calls
CONVERTDATE.
Figure 3.2 illustrates the purpose of the CONVERTDATE Switch stub.
Figure 3.2. HPSWITCHTOCM Example, CONVERTDATE
Recall that Switch stubs make the mixed-mode calling process transparent
to the calling program so that program need not change. This is possible
because the stub procedure name is identical to the CM target procedure
name, and the stub procedure parameters and their types are also
identical to those of the target procedure. This allows the recompiled
application to continue to call CONVERTDATE without any changes to the
source code of the application.
The following sample procedure declarations demonstrate these aspects of
achieving mixed-mode calling transparency. The first extract is the
declaration portion of an SPL procedure in the CM SL:
+---------+
+-| MPE V/E |---------------------------------------------+
| +---------+ |
| |
|PROCEDURE CONVERTDATE (FROMDATE, TODATE, |
| FROMFORMAT, TOFORMAT, DATELENGTH);|
| VALUE FROMFORMAT, TOFORMAT, DATELENGTH; |
| BYTE ARRAY FROMDATE, TODATE; |
| INTEGER FROMFORMAT, TOFORMAT, DATELENGTH; |
| |
| |
+---------------------------------------------------------+
The second extract is the HP Pascal/XL declaration portion of a Switch
stub that enables you to call the corresponding CM SL procedure:
+--------+
+-| MPE XL |----------------------------------------------+
| +--------+ |
| |
|TYPE |
| DATE_BUFFER : PACKED ARRAY [1..80] OF CHAR; |
| |
| |
|PROCEDURE CONVERTDATE(VAR FROMDATE, TODATE : DATE_BUFFER;|
| FROMFORMAT, TOFORMAT, DATELENGTH : SHORTINT);|
| |
| |
| |
+---------------------------------------------------------+
Note the ways in which the MPE XL declaration makes the stub and the
switching process transparent to any program that calls the SPL
procedure:
* The Switch stub procedure name is identical to that of the SPL
procedure.
* The types of the Switch stub parameters are declared to correspond to
those of the SPL procedure:
* Value parameters correspond to value parameters; likewise,
reference parameters correspond.
* The data types of the corresponding parameters are compatible.
Another responsibility of the Switch stub is to set up the parameters
required by the appropriate Switch intrinsic. In this instance, that is
the HPSWITCHTOCM intrinsic. Here, again, is a sample call to
HPSWITCHTOCM:
HPSWITCHTOCM(proc, method, numparms, parms, funcreturnlen,
funcvalue, conditioncode, userstatus)
The parameters that the CONVERTDATE Switch stub must set up before it can
call the HPSWITCHTOCM intrinsic convey to Switch the following
information:
* Name and CM library or 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.
* Array of records, each containing a description of one parameter
being passed, including:
* Pointer to the 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 the parameter in bytes (must be positive integer
<= 2 ** 16).
* For reference parameters, the stub must specify either 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 to be returned from the target procedure.
* Status record to report on HPSWITCHTOCM's operation.
In the course of Example 3-1A, the necessary constants, types, and
variables are declared. The body of the stub follows in Example 3-1B.
Examples 3-1A and 3-1B together constitute the CONVERTDATE stub.
Example 3-1A. Declarations Portion of CONVERTDATE Stub
$standard_level 'EXT_MODCAL'$
$subprogram$
$os 'MPE/XL'$
Program XAMPL31A(input, output);
const { * * * PROC parameter * * * }
{The OS finds procedures by number, by name, or by plabel}
pidt_known = 0; {it is found by number }
pidt_load = 1; {it is found by name }
pidt_plabel = 2; {it is specified by its CM plabel}
{Which library is the procedure in?}
system_sl = 0;
logon_pub_sl = 1;
logon_group_sl = 2;
pub_sl = 3;
group_sl = 4;
{max CM procedure name length}
length_cm_proc_name = 16;
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..length_cm_proc_name]
of char;
{defining type of HPSWITCHTOCM proc parameter}
scm_procedure = packed 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 : (
{system, pub, or group library}
p_lib : bit8_a1;
{ASCII name left justified & blank padded}
p_proc_name : cm_proc_name);
{proc found by proc's CM plabel}
pidt_plabel : ( p_plabel : bit16_a1);
end;
Example 3-1A. Declarations Portion of CONVERTDATE Stub, continued
const { * * * METHOD parameter * * * }
{defining HPSWITCHTOCM method parameter}
method_normal = 0; {non-split-stack}
method_split = 1; {if all parameters within cm stack
or ref parm length < threshold
then use method_normal
else wrap ref parms in extra data
segment }
method_no_copy = 2; {use split stack if ref parms
outside of cm stack }
const { * * * NUM_PARMS parameter * * * }
max_target_parms = 32; {max # of target parameters}
const { * * * PARMS parameter * * * }
{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}
type
{defining type of indicator as byte or word address}
scm_parm_type = bit16;
{defining type of indicator as input and/or output }
{parameter }
scm_io_type = set of ( INPUT_PARM, OUTPUT_PARM );
{defining 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 : scm_parm_type;
{value parm or byte or word address reference parm}
pd_io_type : scm_io_type; {input and/or output}
end;
{defining type of HPSWITCHTOCM parms parameter}
scm_parm_desc_array = array [1..max_target_parms]
of parm_desc;
Example 3-1A. Declarations Portion of CONVERTDATE Stub, continued
const { * * * CONDITION_CODE parameter * * * }
ccg = 0;
ccl = 1;
cce = 2;
type
ccode_type = shortint;
const { * * * STATUS constant * * * }
all_ok = 0; {used in status check}
type { * * * STATUS parameter * * * }
xlstatus = record
case integer of
0 : (all : integer);
1 : (info : shortint;
subsys : shortint);
end;
type { * * * Switch stub parameters * * * }
date_buffer = packed array[1..80] of char;
{declaring intrinsic procedures -- externals}
procedure HPSWITCHTOCM; intrinsic;
procedure HPSETCCODE; intrinsic;
procedure QUIT; intrinsic;
{declaring Switch stub header}
procedure CONVERTDATE (var FROMDATE, TODATE : date_buffer;
FROMFORMAT, TOFORMAT, DATELENGTH : shortint);
var
proc : scm_procedure; {target proc name}
parms : scm_parm_desc_array;
{describes target proc's parameters}
method : integer; {method of call}
nparms : integer; {# of target's parameters}
Example 3-1A. Declarations Portion of CONVERTDATE Stub, continued
{declaring return parameters}
funclen : integer; {length of return value}
funcptr : integer; {pointer to return value}
cond_code : ccode_type; {how condition code returned}
status : xlstatus; {how MPE XL returns warnings}
{declaring local variables}
loc_fromformat : bit16; { FROMFORMAT }
loc_toformat : bit16; { TOFORMAT }
loc_datelength : bit16; { DATELENGTH }
{end example 3-1A, completing the declaration portion}
Example 3-1B. Body of CONVERTDATE Stub
begin
{format and length parameters are copied into local
variables; local copies used since first 4 parms of proc
are put in registers; Switch wants address but value in
register won't have address; to get address, you must
make local copy}
loc_fromformat := FROMFORMAT;
loc_toformat := TOFORMAT;
loc_datelength := DATELENGTH;
{the Switch variables are initialized}
proc.p_proc_id_type := pidt_load; {find proc by name}
proc.p_lib := pub_sl; {look in PUB SL (LIB=P)}
proc.p_proc_name := 'CONVERTDATE '; {procedure name}
method := method_normal; {NOT split-stack callable}
nparms := 5; {number of parameters}
{unless option variable, when option}
{variable, add 1 to nparms }
funclen := 0; {not a function--no return value}
funcptr := 0; {not a function--no return value}
{In the next sequence, "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 and/or output parameter}
{describe FROMDATE -- input by reference}
{determine pointer to parameter's location; addr }
{function takes parameter as argument and returns address}
parms[1].pd_parmptr := addr(FROMDATE);
{determine length of parameter; }
{sizeof function takes parameter as argument and returns}
{number of bytes in parameter }
parms[1].pd_parmlen := sizeof(FROMDATE);
{reference parameter requiring byte address}
parms[1].pd_parm_type := parm_type_byte_ref;
{input parameter}
parms[1].pd_io_type := [input_parm];
Example 3-1B. Body of CONVERTDATE Stub, continued
{describe TODATE -- output by reference}
{determine pointer to parameter's location; addr }
{function takes parameter as argument and returns address}
parms[2].pd_parmptr := addr(TODATE);
{determine length of parameter; }
{sizeof function takes parameter as argument and returns}
{number of bytes in parameter }
parms[2].pd_parmlen := sizeof(TODATE);
{reference parameter requiring byte address}
parms[2].pd_parm_type := parm_type_byte_ref;
{output parameter}
parms[2].pd_io_type := [output_parm];
{describe TOFORMAT -- input by value}
{determine pointer to parameter's location; addr }
{function takes parameter as argument and returns address}
parms[3].pd_parmptr := addr(loc_toformat);
{determine length of parameter; }
{sizeof function takes parameter as argument and returns}
{number of bytes in parameter }
parms[3].pd_parmlen := sizeof(TOFORMAT);
{value parameter}
parms[3].pd_parm_type := parm_type_value;
{input parameter}
parms[3].pd_io_type := [input_parm];
{describe FROMFORMAT -- input by value}
{determine pointer to parameter's location; addr }
{function takes parameter as argument and returns address}
parms[4].pd_parmptr := addr(loc_fromformat);
{determine length of parameter; }
{sizeof function takes parameter as argument and returns}
{number of bytes in parameter }
Example 3-1B. Body of CONVERTDATE Stub, continued
parms[4].pd_parmlen := sizeof(FROMFORMAT);
{value parameter}
parms[4].pd_parm_type := parm_type_value;
{input parameter}
parms[4].pd_io_type := [input_parm];
{describe DATELENGTH -- input by value}
{determine pointer to parameter's location; addr }
{function takes parameter as argument and returns address}
parms[5].pd_parmptr := addr(loc_datelength);
{determine length of parameter; }
{sizeof function takes parameter as argument and returns}
{number of bytes in parameter }
parms[5].pd_parmlen := sizeof(DATELENGTH);
{value parameter}
parms[5].pd_parm_type := parm_type_value;
{input parameter}
parms[5].pd_io_type := [input_parm];
{call the Switch intrinsic to change modes}
HPSWITCHTOCM (proc, method, nparms, parms, funclen,
funcptr, cond_code, status );
{test MPE XL status value and set ccode if not OK }
{HPSETCCODE intrinsic used to pass on result of CM }
{call made by stub; calling program checks returned}
{ccode }
if status.all = all_ok then
HPSETCCODE(cond_code) {from CM proc }
else
QUIT(status.info); {Switch subsystem error}
end; {Stub procedure CONVERTDATE}
BEGIN {dummy outer block}
END. {end example 3-1B}
NOTE For a complete analysis of NM-to-CM Switch stub code, refer to
Chapter 5.
Figure 3.3 illustrates how your CONVERTDATE Switch stub fits into the
flow of control and enables you to access your CM SL CONVERTDATE
procedure:
Figure 3.3. NM--> CM Switch Summary 2
Example 3-2 implements a Switch stub for the BINARY intrinsic. This
example illustrates a stub for a target procedure that has a functional
return value:
NOTE This BINARY stub is only an example. The BINARY intrinsic is
directly accessible from both Compatibility Mode and Native Mode,
without the need of writing a stub to gain access.
Example 3-2. BINARY Intrinsic Switch Stub
$subprogram$
$standard_level 'EXT_MODCAL'$
$tables off$
$code_offsets off$
$xref off$
$type_coercion 'representation'$
$os 'MPE/XL'$
PROGRAM XAMPL32(input, output);
CONST
Pidt_Known = 0; {by number}
Pidt_Name = 1; {by name}
Pidt_Plabel = 2; {by plabel}
System_Sl = 0; {search library}
Logon_Pub_Sl = 1;
Logon_Group_Sl = 2;
Pub_Sl = 3;
Group_Sl = 4;
Method_Normal = 0; {Switch copy mode}
Method_Split = 1;
Method_No_Copy = 2;
Parm_Type_Value = 0; {value parameter}
Parm_Type_Word_Ref = 1; {reference parm, word addr}
Parm_Type_Byte_Ref = 2; {reference parm, byte addr}
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;
Example 3-2. BINARY Intrinsic Switch Stub, continued
SCM_PROCEDURE =
PACKED RECORD
CASE p_proc_id_type : BIT8 of
Pidt_Known:
(p_fill : BIT8_A1;
p_proc_id : BIT16_A1);
Pidt_Name:
(p_lib : BIT8_A1;
(p_proc_name : CM_PROC_NAME);
Pidt_Plabel:
(p_plabel : BIT16_A1);
END; {record}
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; {record}
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}
PROCEDURE HPSWITCHTOCM; INTRINSIC;
PROCEDURE HPSETCCODE; INTRINSIC;
PROCEDURE QUIT; INTRINSIC;
{End of OUTER BLOCK GLOBAL declarations}
Example 3-2. BINARY Intrinsic Switch Stub, continued
FUNCTION BINARY $ALIAS 'BINARY'$
(
ANYVAR F1 : GENERIC_BUFFER;
F2 : SHORTINT;
) : SHORTINT
OPTION UNCHECKABLE_ANYVAR;
VAR
proc : SCM_PROCEDURE;
parms : SCM_PARM_DESC_ARRAY;
method : INTEGER;
nparms : INTEGER;
funclen : INTEGER;
funcptr : INTEGER;
byte_len_of_parm : BIT16;
cond_code : CCODE_TYPE;
status : XLSTATUS;
VAR loc_F2 : SHORTINT;
funcval : SHORTINT;
begin {STUB procedure BINARY }
{ Initialization }
{ Setup procedure information -- name, lib, etc}
proc.p_proc_id_type := Pidt_Name; {by name}
proc.p_lib := Pub_Sl; {library}
proc.p_proc_name := 'BINARY ';
{Setup misc. variables}
method := Method_Normal; {Switch copy mode}
nparms := 2;
{Setup length/pointers for functional return if this }
{is a FUNCTION. Set length to zero, pointer to NIL }
{if this is not a FUNCTION. }
funclen := sizeof(funcval); {A function}
funcval := 0;
funcptr := INTEGER(LOCALANYPTR(ADDR(funcval)));
{Make a local copy of all VALUE parameters }
loc_F2 := F2;
Example 3-2. BINARY Intrinsic Switch Stub, continued
{Build parameter descriptor array to describe each }
{parameter. }
{F1 -- Input Only by Reference }
byte_len_of_parm := F2 * 1;
parms[0].pd_parmptr := ADDR(F1);
parms[0].pd_parmlen := byte_len_of_parm;
parms[0].pd_parm_type := Parm_Type_Byte_Ref;
parms[0].pd_io_type := [Input_Parm];
{F2 -- Input Only by Value }
byte_len_of_parm := 2;
parms[1].pd_parmptr := ADDR(loc_F2);
parms[1].pd_parmlen := byte_len_of_parm;
parms[1].pd_parm_type := Parm_Type_Value;
parms[1].pd_io_type := [Input_Parm];
{Do actual Switch call}
HPSWITCHTOCM
(proc, {procedure info}
method, {Switch copy method}
nparms, {Number of parameters}
parms, {Parm descriptor array}
funclen, {Function return value length}
funcptr, {Address of functional return}
cond_code, {Condition code return}
status); {Switch status code}
binary := 0;
if (status.all <> all_ok) then
BEGIN {Switch subsystem error}
QUIT(status.info);
END {Switch subsystem error}
else binary := funcval;
HPSETCCODE(cond_code);
END; {STUB procedure}
BEGIN {Program Outer Block code}
END. {Program Outer Block code}
{end Example 3-2}
MPE/iX 5.0 Documentation