06.リンケージ規約(サブルーチンを作る)

リンケージ規約(Linkage convention)とは、プログラム間の呼び出しに関する取り決めです。規模の大きいプログラムはいくつかのモジュールに分割して、異なる人によって作成されたりします。各人が好き勝手に作っては上手く動きません。そこでプログラム間のインタフェースに共通のルールを作ります。
MVSでもプログラム間のインタフェースに関して標準的な約束事を取り決めています。MVSで動作するプログラムはこの約束事に従って作ることが重要ですし、知っていなければ(守らなければ)正しく動くプログラムを作ることができません。タイトルには(サブ・ルーチンを作る)とありますが、メイン・ルーチンを作る場合でも同じ事です。アプリケーションとしてはメイン・ルーチンであっても、MVSからは一種のサブ・ルーチンとして扱われるのです。なお、リンケージ規約はアーキテクチャーで定められたものではなく、使用するオペレーティング・システムによって取り決められます。MVSやVSE(MSP、VOS3あるいはXSPも)などのIBM系メインフレーム・アーキテクチャーで動くOSでは、基本的に同じリンケージ規約が使われています。アプリケーションのみならず言語処理プログラムやユーティリティーなどもリンケージ規約に従って作られています。OSや多くのミドルウェアなども、ユーザー・プログラムに制御を渡す(呼び出す)際はリンケージ規約を守ります。

リンケージ規約が取り決めているのは以下のプログラム間インタフェースです。
  • 呼出し先プログラムの入口点アドレスの提示
  • 呼出し元プログラムへの復帰アドレスの提示
  • 呼出し元レジスターの保証
  • パラメーターの通知
  • 復帰コードの通知

MVSのリンケージ規約

入口点アドレス(GR15)

他のプログラムを呼び出す際、呼び出し元プログラムは、呼び出すプログラムの入口点アドレスをレジスター15に格納して呼び出さなければなりません。呼び出されたプログラムは、レジスター15に自分の入口点アドレスが格納されているものとして処理(ベース・アドレスの設定)を行うことができます。
入口点アドレスの先頭ビットは、呼び出し先プログラムのアドレッシング・モード(AMODE)を示します。正しいリンケージ手順であれば、呼び出された時点でPSWのアドレッシング・モードは呼び出されるプログラムのAMODEに切り替わっています。

復帰アドレス(GR14)

他のプログラムを呼び出す際、呼び出し元プログラムは、呼び出すプログラムが自分に戻ってくるための復帰アドレスをレジスター14に格納して呼び出さなければなりません。呼び出されたプログラムは、処理終了後に呼び出し元へ戻る際、呼ばれた時点のレジスター14に格納されているアドレスを使って呼び出し元へ帰ることができます。
復帰アドレスの先頭ビットは呼び出し元プログラムのアドレスモード(AMODE)を示します。正しいリンケージ手順であれば、呼び出された時点でレジスター14の先頭ビットには呼び出し元のアドレッシング・モードがセットされています。

学習用に24ビット・モードのプログラムを作る場合、とりあえずアドレッシング・モードについては気にしなくてもかまいません。まずは「GR15に入口点、GR14に復帰先のアドレスがセットされるのだ」と覚えて下さい。

呼び出し元レジスター保管域(GR13)

レジスターの保管はリンケージ規約の中でも重要です。呼び出されたプログラムは、汎用レジスターを自分の処理のために自由に使ってかまいませんが、呼び出し元に戻る際には一部を除き復元しなければなりません。このためにリンケージ規約では「レジスター保管域」(レジスター退避領域)と呼ばれる18ワード(72バイト)の領域を使って呼び出された時点のレジスター内容を保管することになっています。

最初は少しわかりにくいかも知れませんが、レジスター保管域は下記のように扱います。
  • プログラムを呼び出す側が用意する (CALLする方が用意する)
  • 呼び出されたプログラム側が保管する(サブルーチン側で使う(保管する))
※親(呼び出す側)が用意した領域を子供(呼び出された側)が使うものと考える。

レジスター保管域は、呼び出し元で用意してその先頭アドレスをレジスター13に設定しておきます。呼び出されたプログラムは、呼び出された時点のレジスター13が示すアドレスへ、13番以外のレジスターであるレジスター14と15及びレジスター0から12のレジスター内容を保管しておき、戻る際にそこから復元しなければなりません(*1)。そして、レジスター15以外の全てのレジスターを復元して呼び出し元へ戻ります。
レジスター保管域は複数のプログラムで共用することはできません。呼び出されたプログラムがさらに別のプログラムを呼び出すならば、別のレジスター保管域を用意してレジスター13にて指し示す必要があります。通常は別のプログラム(サブルーチン)を呼ぶ、呼ばないに関わらず新たなレジスター保管域を用意します。新たに用意した保管域の2番目のワードに、呼び出し元の保管域アドレスを格納します。これは、呼び出し元レジスター13の保管でもあります。そして、新たな保管域のアドレスを呼び出し元保管域の3番目のワードに格納します。このようにして、呼び出し元と呼び出し先のレジスター保管域は互いにチェインされます。プログラムに問題が発生した時、このチェインをたどることによって、どのモジュールがどういうレジスター内容でどこまで呼び出されていたかを追跡することができるようになっています。

*1 プログラムが使うレジスターだけを選択して保管してもよいが、使うレジスターと使わないレジスターをいちいち選んで保管、復元するのは面倒だし誤りも起きやすい。よほどの理由がない限りレジスター13以外のすべての汎用レジスターを保管するのが一般的。

パラメーターの通知(GR1およびGR0)

パラメーターは、個々のプログラムの処理内容によって大きさ、個数、内容がバラバラなので規約として取り決めることはできません。しかし、パラメーターの通知方法については汎用的な方法が標準化されています。

COBOLっぽく書くと、CALL “module名” USING パラメーター1 パラメーター2 パラメーター3…として呼ばれたプログラムは、上記のフォーマットでパラメーターが渡ってくる。

パラメーターは1つであれ複数であれ、渡す順番にその値が格納された領域のアドレスをフルワードの領域に並べて格納します。パラメーターのアドレスが格納された領域を「アドレス・パラメーター・リスト」と呼びます。
アドレス・パラメーター・リストは可変長のフルワード領域で、パラメーターの個数*4バイトの長さを持ちます。最後(1つであれば最初で最後の)のパラメーター領域のアドレスを示すワードの先頭ビットを1にすることで、呼び出されたプログラムは何個のパラメーターが指定されたかを知ることが出来ます。呼び出し元プログラムは、アドレス・パラメーター・リスト自身のアドレスをレジスター1に入れてサブルーチンを呼び出します。
また、アセンブラー・プログラム同士であればレジスター0に追加のパラメーターを入れて通知することもあります。MVSやDFPのアセンブラー用プログラミング・サービスでは、そのようなパラメーター通知の方法を採るものもあります。

アドレス・パラメーター・リストによるパラメーターの通知は、リンケージ規約の中でもこれ迄に出てきたGR13~GR15で指定する入口点アドレスと復帰アドレスの通知、レジスター保管域の使用に比べれば絶対に守らなければならないものではありません。アプリケーションなら独自のパラメーター通知方法を使ってもかまいませんし、4バイト以下のパラメーターならアドレスではなく値そのものをパラメーター・リストの中に入れてしまうことも行われます。しかしながら、他言語から呼び出されるアセンブラー・ルーチンや汎用的なサービス・ルーチン、ユーティリティーのようなプログラムを作るなら、標準化されたパラメーター通知方法で設計した方がいいでしょう。COBOLなどの言語環境プログラムやOSの各種ユーティリティーなどもこのフォーマットに沿ってパラメーターを処理しています。

JCLのEXECステートメントのPARMパラメーターで指定したパラメーター文字列は、上図のように渡されます。PARMパラメーターが省略されても同じで、その場合は文字列の長さが0で通知されます。

復帰コードの通知(GR15)

プログラム処理の結果の1つである復帰コードは、レジスター15に入れて呼び出し元に通知します。レジスター15は入口点アドレスで使われますが、呼び出し元へ帰る際は不要です。よって、MVSではGR15を復帰コードの通知レジスターとして使用します。COBOLなどのコンパイラー言語から呼び出された場合でも、レジスター15へ復帰コードを設定します。復帰コードはコンパイラーの実行時ライブラリーによって、然るべき変数(RETURN-CODE等)に設定されます。プログラムがEXECステートメントのPGMパラメーターで指定されて実行されたジョブ・ステップ・タスクのメイン・プログラムであれば、レジスター15に設定された値はジョブ・ステップの完了コードになります。

リンケージ規約で使用するGR0~1及びGR13~15の各レジスターは、リンケージ・レジスターとも呼ばれます。これらのレジスターはプログラム間リンケージで重要な役割を果たすため、壊してはならないと後生大事に抱えているビギナー・プログラマーをたまに見かけますが、プログラムの冒頭で保管域にセーブしているのですから自由に使ってかまいません。それに、MVSやDFSMSdfpのサービスを呼び出せばどうせ壊れます。レジスター13以外の0~1、14~15番レジスターは、作業用のワーキング・レジスターです。積極的に使いましょう。

リンケージ・レジスター一覧
レジスター 用途
GR0 追加のパラメーター等
GR1 アドレス・パラメーター・リスト等
GR13 レジスター保管域のアドレス
GR14 呼び出し元への復帰アドレス
GR15 入口点アドレス、復帰コード

プログラムの入口点処理と呼び出し元への復帰処理のサンプル

  • ①呼び出し元レジスター14~15、0~12を保管。
  • ②入口点アドレスを自分のベースレジスターにセット。
  • ③呼び出し元の保管域アドレスを退避。(この時点でGR15は不要となっている)
  • ④レジスター13に自分のレジスター保管域のアドレスをセット。
  • ⑤呼び出し元保管域のアドレスを、自分のレジスター保管域の第2ワードに退避。(呼び出し元レジスター13の保管)
  • ⑥自分の保管域アドレスを呼び出し元保管域の第3ワードにセット。
  • ⑦JCLのEXEC PARMパラメーター領域のアドレスをロード、GR14と15にパラメーター文字列のアドレスと長さをセット。
  • ⑧復帰コード(完了コード)をレジスター15にセット。
  • ⑨呼び出し元レジスター保管域のアドレスをロード。(レジスター13の復元)
  • ⑩レジスター15を除く呼び出し元レジスターを保管域から復元。(レジスター14と0~12)
  • ⑪呼び出し元プログラムへ戻る。
  • ⑫自分のレジスター保管域の定義。例では初期値-1としているが0でもかまわない(値は何でもよい)

このプログラムはEXECステートメントのPARMパラメーターで指定されたパラメーター文字列を、コンソールに表示する簡単なプログラムです。必要な初期設定を行った後、BAS 10,SHOWPARM命令でSHOWPARMサブルーチンを呼び出してパラメーターをコンソールに表示し、復帰コード0を設定して呼び出し元(MVSのジョブ管理ルーチン)に戻ります。
CSECTアセンブラー命令以下、&#9317までの処理がリンケージ規約に基づく最初のセットアップ処理です。ハウスキーピング処理とも呼ばれます。何種類かの方法がありますが、リンケージ規約に合っていれば使う命令や順序などに決まりはありません。&#9319以降は呼び出し元への復帰処理ですが、これも重要なのはレジスターを呼び出された時の状態に復元するということです。レジスターを元に戻さずに復帰してしまうと、呼び出し元は正しく処理を継続できません。

リンケージ規約によるハウスキーピングの処理には複雑な命令は必要ありません。「マシン命令入門」で紹介した基本となる何種類かの命令でロジックが作れます。プログラムの規模や内容に関係なく、とにかく絶対必要な処理なのでしっかりと覚えて下さい。
「S/370アセンブラー講座」は、ここまでくれば実際にMVSで動かすプログラムを書き始めることができます。まだ使ってない命令やOSのアセンブラー・サービスAPIなど沢山ありますが、残りは必要に応じて調べて行けばいいでしょう。次回からは計算や文字処理、分岐とループなどプログラミングっぽいテーマで解説します。