ハウスキーピングあれこれ

ハウスキーピング(プログラム冒頭での呼び出し元レジスター内容の保管やベースアドレスの設定処理)の処理にはプログラマーによっていろいろな方法が使われますが、基本は「リンケージ規約の遵守」です。決まり切った手順でもあるので一度スタイルを決めればずっと使い続けられます。ここでは私自身が長年使ってきたコーディングのいくつかをサンプルとして紹介します。

オーソドックスなコード

レジスター保管域をハウスキーピングのコードの中に定義した例です。レジスター保管域のように実行中に参照することがないデータ領域にいちいち名前を付けるのは面倒なので私自身は好んで使っていました。呼び出し元への復帰時にはGR15に復帰コードが設定されているものとしています。

モジュールの先頭にヘッダーを付ける

プログラムの冒頭にヘッダーを付けることはしばしば行われます。名前、アセンブル日付と時刻などはヘッダー情報の代表的なものです。複数のプログラムを結合して1つのロードモジュールにするような場合は、各CSECTの名前も入れればダンプ・リストを見た際に識別しやすくなります。商用プログラムでは製品のバージョンやPTF用のパッチ・エリア、COPYRIGHT表示なども入れられます。
一般のプログラムなら入口点ではGR15にプログラムの先頭アドレスが格納されていますから、ヘッダーの長さだけジャンプする無条件分岐命令を先頭に置き続いてヘッダーのデータを書きます。GR15を基点にしてヘッダーの長さ+分岐命令の長さを指定してヘッダーを迂回します。ヘッダーの長さはプログラム設計時に決まり、後で変わることは基本的にないので分岐先を変位で指定していますが、名前を付けて迂回するなら下記のように書けます。

ヘッダー用マクロを作る

いちいちヘッダーを書くのが面倒、プログラムを分割して開発するがヘッダースタイルは統一したいといった場合はヘッダーを生成するマクロ命令を作ると便利です。MACROからMENDで囲まれた部分をMDHDRと言う名前のメンバーで作成します。プログラムではこのMDHDRマクロを書けばアセンブル時にマクロ内で定義したヘッダーデータが展開されます。下記にヘッダーマクロを使ったサンプルを示します。

オーソドックスなコード(再入可能プログラム)

再入可能(リエントラント構造)プログラムは、自分のプログラム内に書き込みデータを持つことができないのでレジスター保管域も定義できません。レジスター保管域をプログラムの外に確保するサンプルです。

GR13をベースレジスターに使う

再入可能である必要がないプログラムではレジスター保管域を示すGR13をベース・アドレスにしたプログラムを作成することもできます。サイズが大きくなるプログラムでも、GR13、12、11とベース・アドレスに使えるレジスターを1つでも多くすることができます。サンプルではレジスター保管域をプログラムの先頭に置き、GR13とGR12をベース・レジスターにしています。レジスター保管域を DC 17F と68バイトしか定義していないのは間違いではありません。先頭の無条件分岐命令自身が長さ4バイトなので、合わせて72バイトになります。保管域の最初のワードはアセンブラー言語では未使用であることを応用してものです。
筆者はバッチ処理プログラムの場合は、好んでGR13をベース・レジスター兼RSAポインターにしていました。たかが72バイト領域の為だけに汎用レジスターを1つ使ってしまうのは勿体ないです。再入可能プログラムの場合は、ローカル作業域の先頭にレジスター保管域を置き、その先頭アドレスをGR13に格納するスタイルを多用しました。再入可能プログラムではタスク等の実行単位毎にローカル作業域を必要としますが、GR13をそのベース・レジスターにすれば72バイト保管域の後ろに約4KBの作業域を展開させることができます。

一切のサブルーチンを呼ばないなら

あまりいいサンプルではありませんが、将来に渡っても絶対サブルーチンを呼ばないのならこのようにレジスター保管域を持たなくてもかまいません。OSのAPIは殆どがSVC呼び出しなのでレジスター保管域を必要としませんが、データセットのアクセスに使うGETやPUTマクロなど、APIによってはGR13がレジスター保管域であることを要求するものもあるので注意します。

リンケージ・スタックを使う

リンケージ・スタックは、ESA/390から利用できる機能です。先頭のBAKR 14,0命令は現PSWの前半ワード内容とGR14の内容で後半ワードを生成した呼び出し元への復帰用PSW、および汎用レジスターとアクセス・レジスターをスタックに保管します。最後のPR命令はスタックに保管されたPSWとレジスター2から14が復元されます。結果としてレジスターを復元して呼び出し元に戻ることになります。レジスター15、0と1は、PR命令発行時の内容がそのまま渡ります。BAKRはスタックへのPUSH、PRはスタックからのPOPです。内部サブルーチンでもレジスターの保管を気にせずに済むので使うと便利な機能です。
MVSではリンケージ・スタックの利用は必須ではありませんし、使う使わないの選択は自由なので自分が呼び出すサブルーチン(OSのAPIを含む)がリンケージ・スタックを使う保証はありません。そのため、GR13には標準のレジスター保管域をポイントする方が間違いありません。この場合、呼び出し元保管域のポインターにはアドレスの代わりに’F1SA’の4文字を設定する規約になっています。
現実的には、プログラムは最初に実行される時にOSが用意したレジスター保管域がGR13で渡されるため、GR13を他の目的で使わない限りわざわざ別に用意しなくても問題はおきないでしょう(例えば、QSAMのGET/PUTマクロの利用など)。ただし、プログラムがABENDした時のダンプには正しいSAトレースが出力されなくなります。自分は呼び出し元の保管域を使用しなくても、呼び出すプログラムのために標準通りの保管域を用意し、互いにチェインする代わりにF1SAを表示するのはレジスター保管域とリンケージスタックを混在させるためのMVSの決まり事なのです。テスト的に作成するプログラムでない限り規約通りにする方がいいでしょう。