03.アセンブラー言語の概説(コーディングの基礎)
プログラムの書式
アセンブラー・プログラムは1行が80バイトです。LRECL=80の固定長レコードのデータセットを作成してそこへ書いていきます。区分データセットのメンバーとして登録するのが普通ですが、ちょっとしたプログラムや、命令の動きだけを確認したいような時は、アセンブラーJCL中にSYSINデータとして直接コーディングすることも多いです。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- label op [operand,operand,…] comment X CHECK01 DS 0H L R1,AREA1 LOAD INPUT VALUE LTR R1,R1 TEST VALUE BNP ERROR1 IF NOT PLUS, DO ERROR PROC-1 * GETMAIN RC, OBTAIN WORKING STORAGE + LV=(1),LOC=ANY LTR R15,R15 SUCCEEDED ? BNZ ERROR2 NO, DO ERROR PROC-2 : :
アセンブラーではラベル(名前)は第1桁目、命令はラベルがあればラベルの後ろに1文字以上の空白を置いてから、ラベルがなければ第2桁目以降から書けます。オペランド(命令のパラメーター)は命令の後ろに1文字以上の空白を置いてから書けます。オペランド(オペランドがない命令では命令)の後ろに1文字以上の空白を置いてからコメントが書ける。1行に書ききれないときは72桁目に空白以外の文字を置いて、パラメーターは次の行の16桁目から、コメントは17桁目以降から継続できます。アセンブラーは最低限のルールを守れば基本的に自由コーディングできます。しかし行によって命令の開始桁が違っていたり、人によってスタイルが変わると見にくいので伝統的な決まり事があります。
73桁目以降はSEQ番号ですが使わなくてもかまいません。
1?8桁目はラベル。命令は10桁目からオペランドは16桁目から書きます。OSサービスのマクロなど、命令が6文字以上ある場合は、パラメーター(オペランド)開始位置もそれに合わせてずらします。その場合でも継続行は必ず16桁目からでなければなりません。MVSのアセンブラーではロングネームが使えるので必ずしもこのルールに沿えませんが、伝統的な基本として知っておいてください。
コメントにはいろいろスタイルがありますが、第41桁目、40桁目、36桁目などから書かれることが多いでしょう。行全体をコメントにするなら1桁目に*を置きます。オペランドがない命令の文にコメントを書く場合は命令とコメントの間にカンマ記号を置く癖をつけるといいでしょう。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- IPK , SAVE CURRENT PSW KEY...
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- *********************************************************************** * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * * * * * ***********************************************************************
アセンブラー言語ではコメントは非常に重要です。命令文ごとにコメントを付けるのが良いのですが、単なる命令自体の解説のようなコメントばかり続くと、くどくなるので注意します。何をしてるのか、何のためにやるのか等を残すように心がけるといいでしょう。上のサンプルのように*記号などでコメント欄をカプセルのように囲むこともよく行われます。きちんとしたコメントは十分なドキュメントにもなります。商用プログラムではコメント・カプセルの中にドキュメント自体を書いてしまうことも少なくありません。
形を繕うための外部ドキュメントにさしたる意味はありませんが、プログラム内のきちんとしたコメントはプログラムの保守を行う上でとても役に立ちます。商用プログラムの場合、どんなに素晴らしいものでもコメントがまったく無いようなプログラムは0点と言っても過言ではありません。アセンブラー言語でプログラミングを行う場合は、学習レベルのプログラムを書くときからコメントを付ける癖をつけることを勧めます。
プログラムはCSECTで始まりENDで終わる
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- PROG1 CSECT , USING *,12 DEFINE BASE REGISTER STM 14,12,12(13) SAVE CALLER REGISTERS LA 12,0(,15) GR12 -> OUR 1ST BASE ADDRESS LR 15,13 SAVE CALLER SAVEAREA : : WORKAREA DC D'0' DOUBLE WORD FOR WORK DATA01A DC F'123' DEFAULT VALUE DROP , FORGET BASE REGISTER END
プログラムの開始はCSECT命令を使います。一般に最初のセクション開始がSTART、2番目以降のセクション開始がCSECTとされますが、特別な理由がない限りSTARTを使う必要はありません。セクションとはプログラムを構成する要素のことで、実行される命令やデータが展開される制御セクション、外部領域内をフィールドに分割して名前で参照できるようにする見かけセクションなどがあります。CSECTは制御セクションのことで実際に命令やデータが展開されてオブジェクト・モジュールとして出力されます。
プログラムの終了はEND命令を使います。END命令にはプログラムの実行開始位置を指定することもできます。省略すればCSECTの先頭から実行されます。実行開始位置をセクションの途中からにする手法はそれほどむずかしいものではありませんが、慣れるまではわかりにくいので素直にプログラムはセクションの先頭から実行されるものなのだ、と覚えていいでしょう。
ロケーションカウンター
LOC OBJECT CODE ADDR1 ADDR2 000000 90EC D00C 0000C 6 BEGIN STM 14,12,12(13) 000004 41C0 F000 00000 7 LA 12,0(,15) 000008 18FD 8 LR 15,13 : 000118 00000064 45 DATA DC F'100' LOCと表示されている箇所がロケーションカウンターである
ロケーションカウンターはオブジェクトモジュール内の命令やデータがCSECTの先頭からどれだけ離れているかを示すもので、オフセット、アドレス、番地などとも呼ばれます。あくまでもプログラム(CSECT)の先頭からの相対アドレスです。実際の仮想記憶域にローディングされると、そのローディング・アドレスが加算されて仮想記憶上の主記憶アドレスを形成します。ロケーションカウンターは命令やデータの長さに応じてアセンブラーが適切な番地を計算しますが、必要に応じてプログラマーが変更することもできます。これによってデータ領域の再定義(Redefine)が可能になります。
アセンブラー命令
アセンブラー言語では命令は大きく3種類あります。1つはCPU命令です。マシン命令、機械命令とも言いCPUの1つ1つの動作を指示するものです。次がマクロ命令です。複数の命令をまとめたもので、繰り返し実行する処理などをいちいち書かなくても、マクロ名を書けば対応する命令列に展開してくれるものです。OSのサービス(API)などの多くはマクロ命令を使って呼び出します。メインフレームのマクロは非常に強力な機能を持っていて、活用すれば高級言語並みのコーディングが可能になります。CPU命令とマクロ命令(1部例外を除く)は最終的にオブジェクトモジュールになります。最後が「アセンブラー命令」です。アセンブラー命令は実際にCPUで実行されるものではなく、アセンブラーに対してアセンブル時の動作を指示するものです。いろいろとありますが覚えなければいけないものはそれほど多くありません。
CSECTとEND
CSECTが制御セクション(プログラム)の開始、ENDが終了を示します。1つのソースプログラム内に複数のCSECTを持つこともできますが、CSECT単位にソースプログラムのメンバーを分けて作成し、リンケージエディタでまとめる方がわかりやすいでしょう。
USINGとDROP
ベースレジスターの設定・解除を行います。ベースレジスターとはプログラム内で分岐先やデータフィールドを名前で指し示す際に、基本となるアドレスがどのレジスターに入っているかをアセンブラーに知らせるために用いられます。通常はプログラムの先頭のアドレスが格納されるレジスター番号を指定します。1つのベースレジスターでアドレスできる範囲は4096バイトです。プログラムが4KB以上の大きさを持つ場合、4KB毎に異なるベースレジスターを用いることになります。USING命令はベースレジスターの設定を行う命令で、オペランドにベースアドレスとそのベースアドレスが格納されたレジスター番号を指定します。DROP命令は設定したベースレジスターの解除を行います。
S/370アセンブラーではベースレジスター、ベースアドレスの考え方は重要なので章を改めて解説しますが、ここではプログラムを書く場合は最低1つのベースレジスターを必要とするのだ、と言うことを覚えておけばいいでしょう。
DC,DSとORG
定数または変数の定義を行います。一般にDC命令は定数、DS命令は変数を定義するものと理解されます。しかしCPUは定数と変数を区別しません。単にデータ領域、データフィールドをプログラム内に定義する命令と考えればいいです。定数で定義しても命令で書き込みをすれば内容は変更できますから実際は変数として扱えます。定義したデータ域に初期値を設定するのがDC命令です。初期値を必要としなくてもDC命令を使う癖をつけるといいでしょう。数値なら0を、文字なら空白を与えます。
000118 00000064 45 A DC F'100' 00011C 46 B DS F
具体例で示すとわかりやすいです。DC命令では領域内の値がアセンブルリストに出てきます。DS命令ではアドレスしか出ません。DC命令ならデータ域の構造が、リストを見れば直感で感じ取れます。DS命令だとデータ型を見ていちいち考えなければなりません。簡単なプログラムではわかりにくいのですが、プログラム内のデータフィールドが増えてきたり、データ構造が複雑になってくると、こんなこともデバッグを楽にする方法の1つになるのです。
DC命令はオペランドで定数のデータ型、長さ、複写回数、初期値を設定します。
DC [複写因数]型[長さ]'定数値' ----+----1----+----2----+----3----+----4----+----5----+----6 LABEL1 DS 0H IC R1,CHAR1 LOAD 1BYTE INTO LOW : CHAR1 DC C'A' A DC CL10'ABCDEFGH' 'ABCDEFGH ' DC 3CL2'XY' XYXYXY DC X'00' x00 DC XL4'123' x00000123 DC F'12345' x00003039 DC FL3'-875' xFFFC95 DC H'3000' x0BB8 DC D'0' x0000000000000000 DC P'12345' x12345C DC P'-875' x875D DC PL3'-875' x00875D DC A(CHAR1) x00000108 DC V(SUBRTN1) x00000000
複写因数は同じ定数を繰り返し定義する時に指定します。繰り返された定数は連続した領域として定義されます。型は定数の種類を示し、文字型、16進数値型、整数型、パック10進数型、アドレス型などがあります。長さは長さモディファイアとも呼ばれ型を修飾する目的で指定します。長さ以外の修飾もできますがここでは解説しません。文字型であれば型だけでは1バイトしか定義されないので、連続した10文字を定義したければCL10となります。整数も長さを指定でき、F型定数はフルワードで4バイトが定義されますが、FL3とすれば3バイトの整数として定義できます。
アドレス型はその名の通りアドレスを定義するために使わます。プログラム内のラベル名を値として指定すれば、そのラベルが示す番地が定数として定義されます。アセンブルの時点ではCSECTの先頭からの相対アドレス(ロケーションカウンター)がセットされ、リンケージの時に、結合される他のモジュールの大きさ・結合順序によってさらに調整されます。実行時にはローディングされた仮想記憶アドレスが加算されて実際の主記憶アドレスが設定されます。これらはアセンブラー、バインダー(リンケージエディター)およびMVSのコンテントスーパーバイザー(CSV)によって自動的になされるためプログラマーは何も考えなくていいです。A型アドレス定数は自モジュール内のラベルを参照する場合に用いられ、V型アドレス定数は外部モジュールの入口点を参照する場合に用いられ外部サブルーチンの呼び出しで利用されます。アドレス定数のように数値をフェーズ毎に変更しなければならないものは再配置ディクショナリー(RLD)と言うものが使用され管理されます。RLDはオブジェクトモジュールにもロードモジュールにも存在します。
タイプ | 長さ | 説明 | 備考 |
---|---|---|---|
C | 1byte | 文字領域(バイト域)を定義する。 | 長さはモディファイアで最大256バイトまで伸ばせる。それを超える場合は複写因数を利用する。CL1024とはできないが1024Cとならできる。1024CL8も可能。 |
X | 1byte | 16進数を定義する。 | 0?Fの16個の16進数を指定できる。16進数2桁で1バイトとなり、x00?xFFまでのすべてのビットパターンを定義できる。 |
F | 4byte | フルワードの整数を定義する。 | 長さモディファイアを使い1?3の範囲で長さの調整が可能。長さを省略した場合、フィールドはワード境界に調整される。 |
H | 2byte | ハーフワードの整数を定義する。 | しばしばDS 0Hとして命令がハーフワード境界に置かれるようなラベル定義にも使われる。複写因数0は境界調整だけが行われ、データは設定されない。 |
Y | 2byte | ハーフワード定数をラベルで定義する。 | 値は”で囲まれた数値でなくラベル名で定義したい場合などに使う。Y(MAXVALUE)とすればMAXVALUEに100と言う値が設定されてる時、H’100’と同じことになる。 |
D | 8byte | ダブルワードの浮動小数点を定義する。 | 実戦では浮動小数点の定義よりは、ダブルワードの作業用フィールドを作成するために用いられることが多い。 |
P | nbyte | パック10進数を定義する。 | 長さモディファイアで任意の長さの10進数フィールドを定義できるが、長さを省略すれば指定した10進数の桁数に応じて適切な長さが取られる。 |
A | 4byte | 命令ラベルや定数のアドレスを定義する。 | 有効範囲は自モジュール内。フルワード定数をラベルで定義する場合にも利用される。 |
V | 4byte | 外部モジュールの入口点アドレスを定義する。 | 外部サブルーチンの呼び出しで利用する。 |
C、X、F、Hあたりが基本中の基本となるデータ型です。
ORG命令はロケーションカウンターを変更する命令です。主にデータ領域の再定義(Redefine)を行うために使用されます。
----+----1----+----2----+----3----+----4----+----5----+----6 EXECPARM DS 0H DC XL102'00' EXECUTE PARMS ORG EXECPARM LENGTH DC H'0' LENGTH OF DATA DATA DC CL100' ' PARM STRING ORG ,
最初のORGでロケーションカウンターをEXECPARMの先頭に戻し、102バイトフィールドを再定義します。その後にロケーションカウンターを元にに戻しています。ORG命令をオペランドなしで使用した場合、ロケーションカウンターはそれまでの最高値に設定されます。これはロケーションカウンターを元に戻すことと同じです。COBOLなどではデータ領域のREDEFINEはよく使われますが、アセンブラーでも簡単にできます。
リテラル定数とLTORG
リテラル定数とはDC命令を使わずに命令のオペランド内でダイレクトに定数を指定するために用いられます。DCの代わりに=を使います。同じ内容の定数であれば重複して定義されることはなく、以前に定義されたものが再使用されます。
----+----1----+----2----+----3----+----4----+----5----+----6 L R1,=F'1' L R2,=H'321' : : LTORG , DATAAREA DS 0D DC CL8'GHYTRX' PACK2 DC P'1234567' END
通常リテラル定数はEND命令によってすべての命令とデータの後ろに展開されます。(この例ではPACK2の後ろ)リテラル定数が集められ展開される所をリテラルプールと呼びます。LTORG命令を使えば任意の位置にリテラルプールを置くことができます。サイズの大きなプログラムなど、ベースレジスターがアドレスできる範囲との関係で、プログラマーがリテラルプールの位置を調整したい場合に用いられます。
EQU
EQU命令は式や数値に名前を付けるために使われます。よく使われるのがレジスター番号の表記です。
----+----1----+----2----+----3----+----4----+----5----+----6 MH R0,=Y(TAX) : : R0 EQU 0 R1 EQU 1 R2 EQU 2 : R14 EQU 14 R15 EQU 15 RA EQU 10 RB EQU 11 TAX EQU 5 END
EQUで数値に名前を付けて定義して、命令ではその名前で値を参照するようにすれば、計算に使う数値が将来変更された時、EQU文だけ直せば、参照している各々の命令を直す必要がありません。
COPY
COPY命令はソースプログラム中に他のメンバーの内容を取り込みます。必ず使うデータ定義、レジスターのEQU定義、サブルーチンなど使い方は自由です。よく使う一連の手続きなどは同じコーディングを繰り返す代わりに別メンバーで登録しておけば、修正が必要になった時などの作業量を減らせます。
----+----1----+----2----+----3----+----4----+----5----+----6 L R1,=F'1' : DATAAREA DS 0D DC CL8'GHYTRX' PACK2 DC P'1234567' COPY REGEQU INCLUDE GPRS EQUATES END
TITLE,PRINT,SPACE,EJECT
いずれもアセンブルリストの制御に使われる命令です。TITLE命令はリストの各ページの先頭につける見出しを設定します。”で囲まれた任意の文字列を見出しとして指定できます。
PRINT命令は印刷様式の制御を指定します。パラメーターにはON|OFF(PRINT命令以降のリストを印刷する|しない)、GEN|NOGEN(マクロ命令内の各CPU命令などを印刷する|しない)、DATA|NODATA(8バイトを超える定数データの内容を全部印刷する|しない)があります。プログラムであればOFFはもちろん、NOGEN指定のアセンブルリストはデバッグの役に立ちません。特別な理由がない限りOFFとNOGENは指定するべきではありません。DATAは定数の定義が主体となるようなモジュールで使われることがあります。通常はPRINT命令はデフォルトのままでかまわないでしょう。
000094 C1C2C3C4C5C6C7C8 53 LONGDATA DC C'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 52 PRINT DATA 000094 C1C2C3C4C5C6C7C8 53 LONGDATA DC C'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 00009C C9D1D2D3D4D5D6D7 0000A4 D8D9E2E3E4E5E6E7 0000AC E8E9F0F1F2F3F4F5 0000B4 F6F7F8F9
SPACE命令はアセンブルリスト中に1行以上の空白行を挿入します。SPACE 2とすれば2行の空白行が入ります。パラメーターを省略すれば1行の空白行が入ります。EJECT命令はリストの改ページを行います。SPACEおよびEJECT命令自身は印刷されません。
いずれの命令もアセンブルリストを見やすくするために使います。ソースプログラム上では意味はありませんが、アセンブラー言語ではアセンブルリストはデバッグ作業の友ですから、見やすいリストは作業効率の向上に繋がります。
----+----1----+----2----+----3----+----4----+----5----+----6 PRINT ON,GEN,NODATA TITLE 'MY ASSEMBLER PROGRAM' : : L R1,=F'1' L R2,=F'1' SPACE 2 ST R1,AREA1 MH R2,=H'8' EJECT , DATAAREA DS 0D : : END
簡単なアセンブラー・プログラムの例
消費税を計算する簡単なプログラムです。どのアセンブラー命令が出てきたか復習してみましょう。計算結果として税込み価格をプログラムの完了コードとして出力します。(OSの仕様で4095円を超えると完了コードでは正しく表示できません)
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- TITLE 'EASY TAX CALCULATOR' MYPROG CSECT , USING *,R12 DEFINE BASE REGISTER LR R12,R15 SET OUT BASE ADDRESS SPACE , L R1,PRICE LOAD PRICE VALUE MH R1,=Y(RATE) CALCULATE TAX SLR R0,R0 D R0,=F'100' ST R1,TAX SAVE TAX AMOUNT SPACE , L R15,PRICE SET COMPLETION CODE = A R15,TAX PRICE + TAX SVC 3 RETURN TO OS * PRICE DC F'1000' PRICE TAX DC F'0' TAX RATE EQU 5 TAX RATE LTORG , EJECT , COPY REGEQU INCLUDE GPRS EQUATES END [REGEQU] ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- R0 EQU 0 R1 EQU 1 R12 EQU 12 R15 EQU 15