04.S/370マシン命令入門
アセンブラー言語におけるプログラムの書式と基本的なアセンブラー命令が理解できるとプログラムを書く準備が整います。しかしアセンブラー言語ではCPUに直接指示を与えるため、プログラムを書くためにはCPUと関連するハードウェアに関する知識がどうしても必要です。ここではアセンブラー・プログラミングに関するCPUのしくみや実際にCPUを動かすためのマシン命令の基礎について解説します。
CPUとPSW
S/360、S/370以降ESA/390および互換アーキテクチャーではCPUは32ビットCPUです。命令セットはIBM社独自のものが採用されています。この講座では現在のzアーキテクチャーの前身である32ビットCPU命令を中心に解説します。zアーキテクチャーではCPUは64ビットになり命令セットも大幅に拡張されましたが従来のアーキテクチャーでサポートされている命令セットはそのまま使用できます。
PSW(Program Status Word)はプログラム状態語と呼ばれ、CPUで現在動いているプログラムの走行状態(実行状況)を示す64ビットの特殊なレジスターで、CPU動作を制御する基本的な情報が格納されています。最初からPSWすべての情報について覚える必要はなく、アセンブラー言語でプログラムを作る上で、デバッグ作業をする上で必要なものを最初に覚えればいいでしょう。PSWのフォーマット
PSWには「 078D0000 00006192 」こんな値が格納されていたりします。
PSWの後半32ビットが次に実行する命令の仮想アドレスを示します。32ビット目がアドレスモードでプログラムが24ビット・31ビットモードのどちらで動いているかを示します。ビットが1なら31ビットモードです。残りの31ビットに命令アドレスが入ります。今実行している命令ではなく、次に実行する命令のアドレスであることを覚えて下さい。後は2バイト目です。例ではx8Dとなっていますが、8が記憶保護キーでPSWキーと言います。PSWキーとメモリー上のキーが一致していれば書き込みができます。次のDが問題プログラム状態を示します。ここがCなら監視プログラム状態(スーパーバイザーモード)です。正確には他のビットとの組み合わせでDやCになるのですが、滅多なことでは他のパターンにはなりません。取りあえずはこれだけでいいでしょう。3バイト目の前半4ビットのうち、第2,3ビットが命令の結果を示す条件コードですが、実際PSWの条件コードを見てデバッグするようなことはあまりありません。
レジスター
CPUには16個の汎用レジスター(General Purpose Register)があります。GPRまたはGRと略され32ビットの長さを持ち、それぞれ番号(0?15)で識別されます。GPRは命令オペランドのアドレス計算、整数演算、論理演算、実行結果の格納などに用いられます。汎用レジスターの他にCPUの制御に使う制御レジスター、クロスメモリー機能で使用するアクセスレジスター、浮動小数点演算に使う浮動小数点レジスター(64ビット)があります。汎用レジスター以外は一般のプログラムでは基本的に使用しません。zアーキテクチャーではCPUに合わせて汎用レジスターと制御レジスターは64ビットに拡張されていますが、これまでの32ビットCPU命令を使う場合はその違いを意識する必要はありません。
+----------+---------+---------+----------+ GPR I 0000 0000 0000 0000 0000 0000 0000 0000 I +----------+---------+---------+----------+ * * bit0 bit31
レジスターのいちばん左端がビット0です、以降右へビット1,2,3と続き右端がビット31になります。※CPUが複数個実装されているマルチCPU構成のプロセッサーでは、レジスターは各々のCPU毎にあります。
主記憶装置(メモリー)へのアクセス
主記憶装置はプログラムとプログラムが使用するさまざなデータを格納します。S/370以降のアーキテクチャーでは仮想記憶システムなので、我々が作り動かすプログラムはすべて仮想記憶上で動作します。実記憶(リアルメモリー)を直接アクセスすることはありません。以降の解説も主記憶装置あるいはメモリーはすべて仮想記憶装置を指します。
主記憶には先頭からアドレス(番地)が振られ、バイト単位でアクセスできます。アドレスはx00000000から始まりx00FFFFFF(16MB)さらにx7FFFFFFF(2GB)へと続きます。
アドレッシング
メモリーをアクセスする命令は、オペランドで操作の対象となるデータがメモリー上のどこにあるかを指し示す必要があります。これがアドレス表現で、命令実行時に実際のメモリーアドレス(実効アドレス)が計算されます。CPU命令ではメモリー上のアドレスは基本的に以下の2つの方法でアドレスが計算されます。
ベース+変位
レジスターに格納されたベースアドレス(基底アドレス)の値に変位(displacement)を加えて主記憶アドレスを求めます。変位はベースアドレスから何バイト離れたところを示すかの数で0?+4095の値が使えます。変位は命令のオペランド中に固定値で示されます。ベースアドレスを格納したレジスターのことをベースレジスターと呼びます。この方式では4ビットでベースレジスター番号、12ビットで変位を示し、アドレス部の命令オペランドは16ビットとなります。
ベースレジスターにx6000が入っていて命令オペランドで変位がx128(296)となっていれば、主記憶アドレスはx6128となります。
「STCK 120(12)」 ベース+変位方式はオペランドをこのように表現します。書式は変位(ベースレジスター番号)です。12番レジスターの内容に120を加えた数がアドレスになります。人間がいちいち変位を計算するのは面倒なので、普通は変位(ベースレジスター番号)を直接書くよりはラベル名で示します。
「STCK AREA」 のようにオペランドにラベル名が書かれた命令はアセンブラーによって STCK 120(12) と翻訳されます。命令を翻訳する時、どれをベースレジスターに使うかはUSING命令で決まります。USING LABELA,8とすれば、LABELAがベースアドレスでベースレジスターが8番です。USING *,12とすれば、USING命令を書いた場所がベースアドレスでベースレジスターは12番となります。基本的にプログラムは最低でも1つのベースレジスターを使います。通常はプログラムの先頭をベースアドレスにします。ベースレジスターは0以外であればどのレジスターでもいいのですが、1,2,13,14,15は避け3?12番の中から選びます。どれを使うかは最初に習ったり、手本にしたりしたプログラムのスタイルで決まることが多いようです。
アセンブラーはUSING命令で指定したベースレジスターを使って命令を翻訳しますが、指定されたレジスターにベースアドレス値を設定することまではしません。アセンブラー命令とCPU命令の違いがわかっていないとこう勘違いしてしまうかも知れません。アセンブラーのUSINGは命令オペランドをラベル名で書けるようにするだけです。USING命令で指定したベースレジスターに正しいベースアドレス値を設定するのはプログラマーの仕事で、どのプログラムも最初にこれを行います。
ベース+インデックス+変位
ベース+変位のアドレス計算にさらにインデックスアドレスを加えたものです。
レジスターに格納されたベースアドレス(基底アドレス)とインデックスアドレスの値に変位(displacement)を加えて主記憶アドレスを求めます。変位はベース+インデックスアドレスから何バイト離れたところを示すかの数で0?+4095の値が使えます。変位は命令のオペランド中に固定値で示されます。インデックスアドレスを格納したレジスターのことをインデックスレジスターと呼びます。この方式では4ビットでインデックスレジスター番号、4ビットでベースレジスター番号、12ビットで変位を示し、アドレス部の命令オペランドは20ビットとなります。
ベースレジスターにx6000が、インデックスレジスターにx2000が入っていて命令オペランドで変位がx128(296)となっていれば、主記憶アドレスはx8128となります。
「LA R1,120(10,12)」 インデックス+ベース+変位方式はオペランドをこのように表現します。書式は変位(インデックスレジスター番号,ベースレジスター番号)です。10番および12番レジスターの内容に120を加えた数がアドレスになります。
アセンブラーではラベル名のみでオペランドを指定した場合、インデックスレジスターは使用されません。インデックスレジスターを使うには「LA R1,AREA(10)」のようにインデックスレジスターを使用することを明示する必要があります。
インデックスレジスターはすべての命令で使えるわけではなく使用できる命令が決まっています。またベース+インデックス+変位方式の命令でもインデックスレジスターの使用は任意で、使わずにベース+変位でのみアドレス計算をするなら、インデックスレジスター番号に0を指定します。(あるいは省略)
S/370ではレジスター0番はベースレジスター、インデックスレジスターどちらとしても使用することはできません。オペランドで指定した0は0番レジスターではなく、数としての0そのものを意味します。「STCK 120(0)」「LA R1,120(0,0)」はいずれも主記憶アドレス120番地を指します。これはとても重要なことなのでよく覚えておいて下さい。
S/360やS/370アーキテクチャーにおける命令のオペランド・アドレスはプログラム内に直接持たれない、間接アドレッシング方式です。ベースレジスターの値を変えることで、プログラムを例えば主記憶の5000番地、10000番地あるいは32080番地など、主記憶のどこにでも配置できるようになります。これによってプログラムはリロケータブル(再配置可能)な性質を持つことが出来ます。仮想記憶システムが登場する以前の、実記憶上でプログラムを直接動かしていた時代では大きなアドバンテージでした。実効アドレスを求めるためのアドレス表現が複雑に見えますが、実記憶装置をいかに効率よく使い、いかに少ない長さでオペランドを表現するかと言うことで考え抜かれた設計なのです。
アドレッシングモード
レジスターは32ビットなのでフルビット使えば実効アドレスの範囲はは0?4GB-1となりますが、S/360システムでは下位24ビット(3バイト)を主記憶アドレスに使用しました。24ビットでは0?16MB-1です。当時のメモリー容量は数十?数百KB、大きくても数MBでしたから、32ビット全部をアドレスに使うよりは、1部を他の目的に使おうと言う発想は自然でした。S/370システムでも24ビットアドレスはそのまま踏襲され仮想記憶も16MBの大きさを持ちました。基本的にプログラムはx00000000?x00FFFFFFの範囲でしか主記憶をアクセスできません。これが24ビット・アドレッシングモードです。先頭の1バイトは主記憶アドレスとしては無視されるため、メモリー容量が小さかった当時はメモリーを節約するために、この部分にさまざまな制御用の情報をフラグバイトとして持ちました。4バイトあればXL1’フラグ’+AL3(アドレス)として使われることも多く、MVSでは今でも随所にこのパターンが残っています。
さすがに主記憶容量が16MBではどうにもならなくなったため、仮想記憶の大きさは31ビットで表現できる2GBに拡張されました。32ビットにしなかったのは24ビットモード・プログラムに対する互換性のためです。32ビットのうち、先頭ビットをアドレスモードの判定用に用い、残りの31ビットを主記憶アドレスに使いました。この場合プログラムはx00000000?x7FFFFFFFの範囲で主記憶をアクセスできます。これが31ビット・アドレッシングモードです。PSWのアドレス部(後半32ビット)の先頭ビットが0であれば24ビットモード、1であれば31ビットモードとなります。
x00006000 → 24ビットモード
x80006000 → 31ビットモード
特別に指定しない限り、アセンブラー言語で作成したプログラムは24ビット・アドレッシングモードのプログラムになります。まずは基本となるこのモードでプログラミングを覚え、それから31ビット・アドレッシングモードへ進む方がいいでしょう。もし新しいアプリケーションをこれからアセンブラー言語で作るなら最初から31ビットモード(あるいは64ビットモード)のプログラムで覚えてしまってもいいのですが、実戦でのアセンブラー言語はOSやパッケージソフトウェアの出口ルーチンを作ったり、過去に作られた古いプログラムの改良や他言語への書き換え、と言ったことに使われますから24ビット、31ビットの両方のアドレッシングモードを理解して必要に応じて使い分けられるようにすべきです。またそれは今後64ビット・アドレッシングモードを理解する上で必ず役に立つはずです。
境界調整
データや命令をメモリー上の整数倍アドレスから配置することを境界調整(バウンダリー合わせ)と言います。例えばフルワードの整数は4の倍数で始まるアドレス、ハーフワード整数は2の倍数で始まるアドレスにデータを揃えることです。DCやDS命令を使用して定義されたデータはアセンブラーが必要に応じて境界調整してくれます。
なお現在のCPUにはバイト式オペランド機構(byte oriented operand)が備わっているため、フルワード整数が2の倍数や奇数のアドレスで始まっていてもエラーにならずに実行されます。しかしきちんと境界調整がなされている場合に比べて若干のオーバーヘッドが生じます。ただし命令は必ずハーフワード境界に調整されていなければなりません。命令が奇数番地に置かれるとその命令を実行しようとした時に、プログラムは指定例外(S0C6)でABENDします。命令と命令の間にキャラクター型のデータを挟むような場合は特に注意が必要です。アセンブラーのDS 0HやCNOP命令で明示的に境界調整を行います。
とりあえずは実行命令の列の中でフルワード境界に調整する必要があるならCNOP 0,4、データ域の直後に命令を置く場合はDS 0Hと覚えておけばいいでしょう。特に命令についてはデータ領域の直後であろうがなかろうが、命令自身にラベルを付けるのではなく、直前にラベル付きのDS 0Hを置きます。普段からそういう癖をつけるとよいでしょう。CNOP命令の詳細はアセンブラーのマニュアルを参照して下さい。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- : CNOP 0,4 ADJUST TO FULL WORD BOUNDARY BAL 13,*+4+72 AROUND GPRS SAVE AREA DC 18F'-1' OUR GPRS SAVEAREA : : B LABELX DO PROCEDURE 'X' DC CL3'ABC' DEFAULT VALUE SPACE , LABELX DS 0H L 0,FWAREA LOAD INPUT TEST VALUE LABELX L 0,FWAREA この書き方は誤りではないが良くはない :
命令の形式
CPU命令は2,4,6バイトのいずれかの長さを持ち、命令によってその長さが決まっています。オペランドの内容によって長さが変わることはありません。また命令には形式があって命令長やオペランドのフォーマットなどは形式によって定められています。同じ命令なら形式もオペランドの内容によって変わることはありません。現在のzアーキテクチャーでは命令セットが増えたため命令の形式も増え約20種類近くもあります。ここでは基本となる6つの命令形式を紹介します。
RR形式
+---------+----+----+ I OP-CODE I R1 I R2 I +---------+----+----+ 0 7 8 11 12 15bit
命令長は2バイトで先頭1バイトが命令コード、オペランドは1バイトで第1オペランド、第2オペランドのレジスター番号をそれぞれ4ビットで示します。
レジスター同士のデータ移動や演算処理に使われる命令です。
オペランドにアドレスが指定される場合は、レジスターに直接格納されます。これはRR形式アドレスとも呼ばれます。
RX形式
+---------+----+----+----+--------------+ I OP-CODE I R1 I X2 I B2 I D2 I +---------+----+----+----+--------------+ 0 7 8 11 1215 1619 20 31bit
命令長は4バイトで先頭1バイトが命令コード、オペランドは3バイトで4ビットの第1オペランドのレジスター番号、4ビットの第2オペランドのインデックスレジスター番号、4ビットの第2オペランドのベースレジスター番号、12ビットの第2オペランドの変位を示します。
レジスターと主記憶間のデータ移動や演算処理に使われる命令です。第2オペランドのアドレスはベース+インデックス+変位で計算されます。これはRX形式アドレスとも呼ばれ、d2(x2,b2)のように表現されます。
RS形式
+---------+----+----+----+--------------+ I OP-CODE I R1 I R3 I B2 I D2 I +---------+----+----+----+--------------+ 0 7 8 11 1215 1619 20 31bit
命令長は4バイトで先頭1バイトが命令コード、オペランドは3バイトで4ビットの第1オペランドのレジスター番号、4ビットの第3オペランドのレジスター番号、4ビットの第2オペランドのベースレジスター番号、12ビットの第2オペランドの変位を示します。
複数レジスターと主記憶間のデータ移動やシフト、分岐処理などに使われる命令です。
SI形式
+---------+---------+----+--------------+ I OP-CODE I I2 I B1 I D1 I +---------+---------+----+--------------+ 0 7 8 15 1619 20 31bit
命令長は4バイトで先頭1バイトが命令コード、オペランドは3バイトで8ビットの第2オペランドの即値、4ビットの第1オペランドのベースレジスター番号、12ビットの第1オペランドの変位を示します。
1バイトの固定値と主記憶間でのデータ移動や、論理演算処理などに使われる命令です。
SS形式
+---------+---------+----+--------------+----+--------------+ I OP-CODE I L I B1 I D1 I B1 I D1 I +---------+---------+----+--------------+----+--------------+ 0 7 8 15 1619 20 31 3235 36 47bit +---------+----+----+----+--------------+----+--------------+ I OP-CODE I L1 I L2 I B1 I D1 I B1 I D1 I +---------+----+----+----+--------------+----+--------------+ 0 7 8 15 1619 20 31 3235 36 47bit
命令長は6バイトで先頭1バイトが命令コード、オペランドは5バイトで8ビットの第1オペランドの長さ(または4ビットずつでそれぞれ第1および第2オペランドの長さ)、4ビットの第1オペランドのベースレジスター番号、12ビットの第1オペランドの変位、4ビットの第2オペランドのベースレジスター番号、12ビットの第2オペランドの変位を示します。
主記憶間でのデータ移動や、パック10進数の演算処理などに使われる命令です。
S形式
+-------------------+----+--------------+ I OP-CODE I B1 I D1 I +-------------------+----+--------------+ 0 15 1619 20 31bit
命令長は4バイトで先頭2バイトが命令コード、オペランドは2バイトで4ビットの第2オペランドのベースレジスター番号、12ビットの第2オペランドの変位を示します。(2番目のオペランドとなっていますが、実際のオペランドは1つしかありません)
TODクロック値の格納などの制御機構に関する取り扱い命令です。PSWキーの変更やチャネルへの入出力指令など特権命令の多くがこの形式を持ちます。
命令形式を知らなくてもアセンブラー言語でのプログラミングはできます。ただしデバッグ作業でダンプリストの解析をしたり、意図しない実行結果となった時に改めてアセンブルリストをトレースする際には命令形式の理解は必要になります。特にCPU命令のオペランドを間違った時、構文自体が正しければアセンブラーはエラーにしません。そのためソースプログラムだけを追っていては誤りに気づかないことは多いのです。命令形式がわかっていれば、マシンコードに翻訳されたCPU命令を見て、元のソースの命令記述の誤りに気づけるようになります。
命令形式を理解する早道はとにかくアセンブルリストを見ることです。翻訳されたマシンコードと自分が書いた命令を対比させて読む癖をつけます。デバッグには必ずアセンブルリストを使い、元のソースプログラムはプログラム修正作業以外では絶対に見ない、を徹底すれば命令形式や具体的な命令コードはすぐにわかるようになります。プログラミングをやって見ればわかりますが、実戦で作成するプログラムであっても使用する命令の種類はそう多くないのです。せいぜい数十個です。これはプログラムの規模にあまり影響を受けません。またマシンコードから命令記述をイメージする、ハンド逆アセンブルができるようになるとデバッグ作業の効率は飛躍的に向上します。
レジスターとメモリーアクセスに関する基本的な命令
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- LR r1,r2 レジスターのコピー LTR r1,r2 レジスターのコピーとテスト LA r1,d2(x2,b2) アドレス値のロード L r1,d2(x2,b2) レジスター←メモリー(フルワード) LM r1,r3,d2(b2) 複数レジスター←メモリー(フルワード) LH r1,d2(x2,b2) レジスター←メモリー(ハーフワード) ST r1,d2(x2,b2) レジスター→メモリー(フルワード) STM r1,r3,d2(b2) 複数レジスター→メモリー(フルワード) STH r1,d2(x2,b2) レジスター→メモリー(ハーフワード)
LR,LTR
レジスター間でのデータのコピーです。r2で示されるレジスターに入っている内容がそのままr1で示されるレジスターにロードされます。r2で指定されたレジスターの内容は変わりません。LTRの場合はロードされる値の符号によって条件コード(0,+,-)がセットされます。LTRでは同じレジスター番号を指定して、レジスター内の値がゼロか否かを判定することがよく行われます。
----+----1----+----2----+----3----+----4----+----5----+----6----+ LTR 15,15 FUNCTION SUCCEED ? BNZ ERROR NO, DO ERROR PROCESSING :
LA
第2オペランドで示されるアドレスが計算されて、その値がr1で示されるレジスターにロードされます。アドレスの計算とは「b2で示されるレジスター内の値+x2で示されるレジスター内の値+d2で示される変位の値」です。命令の動きとしてはこういう解説になりますが、要は数値そのものをレジスターにセットする命令です。アドレスとなっていますが「アドレス」にこだわる必要はありません。0?x7FFFFFFF(またはx00FFFFFF)までの正の整数値をロードでき、加算命令の代わりにもよく使われます。
※LA命令はアドレスモードによって動作が変わります。24ビットモードではr1レジスターに格納される値の先頭バイト(ビット0?7)が、31ビットモードではr1レジスターに格納される値の先頭ビット(ビット0)が、0にクリアーされます。
----+----1----+----2----+----3----+----4----+----5----+----6----+ LA R1,AREA R1 = Address of 'AREA' LA R1,123 R1 = 123 LA R1,10(,R1) R1 = R1 + 10 :
L,LH,ST,STH
LとLH命令は第2オペランドで示される主記憶の内容が、r1で示されるレジスターにロードされます。Lはフルワード、LHはハーフワードです。LHの場合はレジスターの下位2バイトにロードされます。この時、読み込まれる値の先頭ビットは符号ビットと見なされ、0なら0が、1なら1がレジスターの上位2バイトにも設定されます。32767(x7FFF)を超える値をLHで読み込むとレジスターはx00008000ではなくxFFFF8000となる事を知っておく必要があります。32768(x8000)以上の値を符号無しハーフワードとしてレジスターにx00008000として読み込む場合にはICM命令を利用します。
STとSTHはr1で示されるレジスターの内容を、第2オペランドで示される主記憶にストアします。STHはLHと異なりレジスターの下位2バイトの内容がそのまま2バイトでストアされます。
----+----1----+----2----+----3----+----4----+----5----+----6----+ L R1,AREA R1 = Content of 'AREA' STH R1,HWAREA HWAREA = Low order 2bytes : of GR1
LM,STM
LMとSTMは複数のレジスターと主記憶間でのロードとストアを行います。LM命令はr1とr3で示される範囲の複数のレジスターに、第2オペランドで指定される主記憶の内容がそれぞれロードされます。r1が5、r3が8ならば、主記憶アドレス+0のフルワードがGR5へ、主記憶アドレス+4のフルワードがGR6へ、主記憶アドレス+8のフルワードがGR7へ、主記憶アドレス+12のフルワードがGR8へとロードされます。r3で示されるレジスター番号がr1で示されるレジスター番号より小さい場合、15から0へ循環して使用するレジスター範囲が決まります。例えば14,12とすれば14,15,0,1,?,11,12と13以外のレジスターが操作の対象になります。STM命令はLMとは逆に複数のレジスターの内容を主記憶にストアします。
----+----1----+----2----+----3----+----4----+----5----+----6----+ STM R14,R12,12(R13) Save all GPRS to area points GR13 base address + 12 LM R2,R5,PLIST Load parameter address list into GR2,GR3,GR4,GR5
プログラム割込み
命令の使い方を誤るとCPUは割込みを起こします。これはプログラム割込みと呼ばれます。発生した内容に応じてコードが設定されていて、割込みによってOSに通知します。一般にプログラム割込み=バグと考えられていますが、正確にはそうではありません。
多くの割込みはプログラム・エラーではあるのですが、「仮想アドレスから対応する実記憶を参照しようとしたら、そこにページが無かった」と言うケースもあります。これはページ変換例外と言う割込みで通知されます。MVSはこの割込みを受けると、本当にページが無いのか?実際はページアウトされているだけか?などを調べます。ページアウトされていれば、そのページを読み込めばいいのですからプログラムの実行をエラーにはしません。対応するページが本当に無い場合はアドレスが誤っているのですからMVSによってプログラムはエラーにさせられます。このような時、MVSはCPUからの割込みコードをOSのシステム完了コードに変換してプログラムを異常終了させます。これが S0Cx または S0Dx と言うABENDです。xの部分はCPUの割込みコードに基本的に対応しますが例外もあります。我々は通常のプログラムではCPUからの割込みコードを直接意識しませんので、ここではMVSのABENDコードで解説します。
コード | 理由 | 説明・備考 |
---|---|---|
S0C1 | 命令例外 | CPUに定義されていない命令を実行しようとした。誤ってデータ領域に飛んでってしまった場合や、アセンブル・エラーに気づかずに作ってしまったロードモジュールを実行した等が考えられる。 |
S0C2 | 特権命令例外 | 問題プログラム(一般のプログラム)が特権命令を実行しようとした。監視プログラム状態で呼び出さねばならないサブルーチンを誤って呼び出した、あるいは誤ってデータ領域にジャンプしたら、たまたまそこに特権命令と同じコードのデータがあった等が考えられる。 |
S0C3 | 実行例外 | EX命令で再度EX命令を実行しようとした。 |
S0C4 | 記憶保護例外 アクセス例外 |
誤った主記憶アドレスを参照しようとした、キーが異なる領域に書き込みをしようとした、リンクし忘れたサブルーチンを呼び出した(0番地へジャンプ)、等が考えられる。ABENDコードとしてはとてもポピュラーですが、規模が大きく複雑なプログラムでは原因がなかなかわからない、プログラマー泣かせのABENDでもあります。永久ループに入ろうとしたが、メモリーアドレスをずらしながらのループだったので、たまたま自分のプログラム領域の終わりで引っかかってABENDとなった、と言うのもありがちなケース。 |
S0C6 | 指定例外 | 命令やオペランドの指定方法に誤りがある。奇数番地にある命令を実行しようとした(間違って奇数番地にジャンプ)、奇数番号のレジスターで乗除算を行おうとした、等の原因がある。 |
S0C7 | データ例外 | 10進数演算命令等で符号や数値に誤りがある。本来数字でなければいけないデータに誤って文字(A?Zや空白、記号など)が含まれていることが原因で起きやすい。入力データの妥当性のチェックが甘いプログラムでよく起こす。 |
S0C9 | 除算例外 | 割り算の結果、商が大きすぎてレジスターに入らない、除数が0になっている。最も多いのが0で割ろうとするケース。 |