アドレスモードによる分岐命令の動きの違い
アセンブラー講座のカテゴリーに載せるようなものですが、内容がシステムプログラマー向けなのでここにします。
(これに限らずこのカテゴリーに載せている物はほとんどが昔自分用に作った備忘録なので内容はランダムです。)
アセンブラーでは複数の分岐命令を使い分けますが、時々「これってこれで良かったよな?」と迷ったりしました。それで動きの違いを改めて表にまとめたものです。これが出てきたので紹介します。分岐命令の第1オペランドの命令実行後の内容をアドレスモードの違いでまとめたものです。(64ビットモードは除く)
24ビットモード | 31ビットモード | |
BAL BALR |
先頭バイトにはILC+CC+PGMマスクが入る。ILCビットにはx10のパターンがあるので先頭ビットは必ずしも0になるとは限らない。 | 先頭ビットは常に1になり、残り31ビットにアドレスが入る。 |
BAS BASR |
先頭バイトは常に0になり、残り24ビットにアドレスが入る。 ただしアドレスモードは命令発行時のまま変わらない。 |
先頭ビットは常に1になり、残り31ビットにアドレスが入る。 ただしアドレスモードは命令発行時のまま変わらない。 |
BASSM | 現PSWのビット32?63が、そのまま指定されたレジスタに保管される。 新しいアドレスモードは分岐先アドレスのビット0で決まる。 |
現PSWのビット32?63が、そのまま指定されたレジスタに保管される。 新しいアドレスモードは分岐先アドレスのビット0で決まる。 |
BSM | 現PSWのビット32のみが、指定されたレジスタのビット0に保管される。ビット1?31は変わらない。第1オペランドがレジスタ0の場合は現アドレスモードビットすら挿入されることはない。 新しいアドレスモードは分岐先アドレスのビット0で決まる。 |
現PSWのビット32のみが、指定されたレジスタのビット0に保管される。ビット1?31は変わらない。第1オペランドがレジスタ0の場合は現アドレスモードビットすら挿入されることはない。 新しいアドレスモードは分岐先アドレスのビット0で決まる。 |
BAKR | 現PSW,GPR0?15,AR0?15をリンケージスタックに保管する。第1オペランドがレジスタ0の場合は保管される現PSWのアドレス部は現行値のままである。レジスタが0でなければ、そのレジスタ内容がPSWのアドレス部として保管される。 | 現PSW,GPR0?15,AR0?15をリンケージスタックに保管する。第1オペランドがレジスタ0の場合は保管される現PSWのアドレス部は現行値のままである。レジスタが0でなければ、そのレジスタ内容がPSWのアドレス部として保管される。 |
改めて見るとアドレスモードによって動きに違いが出るのはBAL/BALR,BAS/BASRだけでした。
- x00:命令長が不明のとき
- x01:2バイト命令
- x10:4バイト命令
- x11:6バイト命令 となっている。
ILC(Instruction Length Code)に関する追記
PSWには次に実行すべき命令アドレスが保持されている。しかしながら割り込みが生じた時にはその時実行していた命令アドレスを求めたい場合がある。その時に使用されるのがILCフラグである。PSWが示す命令アドレスからILCが示す命令長を引けば実際に実行していた命令のアドレスがわかるようになっている。ILCには命令長そのものでなくフラグで長さが示されており、
そのため24ビットモードでBAL/BALRを実行すると、第1オペランドが示すレジスタの先頭ビットはBALなら1,BALRなら0となり、必ずしも命令発行時のアドレスモードを示すとは限らないわけである。もっとも外部サブルーチンであればほぼ100%BALRとなるであろうが、先頭バイトにはCC+PGMマスクも入ってしまうため、呼び出されるモジュールが31ビットモードの場合、そのモジュールが呼び出し元へ戻る際にBSM命令を使うと先頭バイトもアドレスの1部とみなされABENDS0C4となるか、まったく別の場所へ分岐してしまうかになる。だから31ビットモードプログラミングにおいて他モジュールを呼び出すためにBAL/BALR命令を使ってはならないのである。
ただしILCがセットされることを応用して意図的に先頭ビットに1を設定してマイナス値であることを示す目的でBAL命令を使う手法もある。例えばGETMAIN(SVC10)マクロがそうである。SVC10は指定された領域アドレスがマイナスならGETMAIN、そうでなければFREEMAINというAPIになっている。そのためGETMAINマクロでは展開の中でBAL命令を使用してレジスターの先頭ビットに1が設定されるようにしている。