08.文字(文字列)の操作

S/370アーキテクチャーではバイト(8ビット)が情報の基本単位です。EBCDICコードもそうですが、1文字は1バイトで示されます。事務処理や制御系のプログラムでは四則演算よりも文字や文字列の取り扱いの方が多いでしょう。S/370はバイト操作に関する命令も豊富に持っています。

バイト転送命令

IC、ICM、STC、STCM

IC(Insert Character)命令は、第2オペランドで指定された主記憶の1バイトを第1オペランドで指定されたレジスターの最下位(右端)バイトに格納します。上位3バイトは変更されません。
ICM(Insert Characters under Mask)命令は、第2オペランドで指定された主記憶の連続したnバイトを第1オペランドで指定されたレジスターのm3で示されるマスクビットに対応したバイトに格納します。マスクビットは4ビットで、それぞれのビットはレジスターの各バイトに対応します。例えば、マスクビットが b’1001′ であればレジスターの第1および第4バイト(いちばん左端と右端)に、b’0011′ であればレジスターの第3および第4バイト(下位2バイト)に格納されます。文字が格納されなかった位置のバイト内容は変更されません。

ICM命令のマスクビットは 15、X’0F’、B’1111′ のように10進、16進、2進のいずれでの表記も可能です。レジスターのどのバイトに対応させてるかが一目でわかるため、B’0000’の表記がわかりやすいでしょう。符号無しハーフワード2進数をレジスターにロードする場合、値が32768以上になるとLH命令では正しくロードできません。ICM命令をマスクビットB’0011’で使用します。
ICM命令では条件コードがセットされます。

0 … レジスターに挿入されたデータの全てのビットが0、またはマスクビットが全て0
1 … 挿入されたデータの左端ビットは1
2 … 挿入されたデータの左端ビットは0
3 … 使用されない

STC(Store Character)およびSTCM(Store Characters under Mask)命令は、IC、ICMとは逆にレジスターから主記憶へのバイト転送動作です。マスクビットの意味は同じです。

MVI

MVI(Move Immediate)命令は、第2オペランドで指定した1バイトの即値(固定値)を、第1オペランドで指定された主記憶の1バイトに格納します。

MVC

MVC(Move Character)命令は、第2オペランドで指定された主記憶の内容を第1オペランドで指定された主記憶に転送します。転送される長さは最大256バイトで、第1オペランド側で指定します。文字の転送は左から右に1バイトずつ順に行われ、指定した長さ分の転送が終了すると命令は完了します。オペランドが重複する場合を除き、第2オペランド側の主記憶内容は変更されません。

上記サンプルは、1バイトずつ転送されることを利用して領域を特定の文字や値で初期化する例です。

MVCL

MVCL(Move Long)命令は、256バイトを超える長さのデータを転送します。第2オペランドで指定された主記憶の内容が、第1オペランドで指定された主記憶に転送されます。転送される長さは受け側、送り側それぞれのオペランドに指定します。受け側<送り側の時は受け側の長さ分だけ転送され、受け側>送り側の時は、送り側の長さ分だけ転送されて受け側の余った領域には指定した充填文字(Padding character)が埋められます。MVCLも文字の転送は左から右に1バイトずつ順に行われます。
オペランドの指定は少々複雑です。送り側と受け側領域それぞれのアドレスおよび長さを、4つのレジスターを使って指定します。第1オペランドは受け側です。r1レジスターにアドレス、r1+1レジスターに長さを格納します。第2オペランドは送り側です。r2レジスターにアドレス、r2+1レジスターに長さを格納します。長さの最大値は16MB-1(2^24)です。第2オペランド側r2+1レジスターの先頭バイトには充填文字を格納します。レジスターは乗除算同様に偶数番号レジスターと奇数番号レジスターをペアにして使い、必ず偶数側のレジスター番号を指定します。

256バイト以上の領域間のデータ転送と領域クリアーの例です。領域をクリアーするには送り側の長さを0にすればいいです。この場合、対になっている偶数レジスターにはどんな値が入っていてもかまいません。長さが0の場合は、送り側記憶域アドレスとして参照されません。
MVCL命令は複雑なので簡単な使い方だけ紹介しました。MVC命令と異なり、命令実行中や実行後にレジスター内容が変更される、条件コードが設定される、誤ったオペランド重複はリジェクトされる(*1)等の特性があります。詳細は命令のリファレンス・マニュアルを読んで下さい。

*1 前述のMVCのサンプルでは256バイトの領域を空白埋めする例を示したが、同様のことをMVCLでやろうとしてもオペランド重複として拒否されてしまう。

変換・探索・編集命令

以降の命令は、アセンブラー・ビギナーには扱いが面倒なものです。こんなものもあるのかという程度で名前だけ覚えるぐらいでもいいです。必要に迫られたら、リファレンス・マニュアルなどを参考にしてトライして見て下さい。

TR、TRT

TR(Translate)命令は、その名の通り主記憶上のデータを変換する命令です。第1オペランドで指定された主記憶の内容が、第2オペランドで指定されたテーブル(変換表)に従って変換されます。変換される長さは最大256バイトで、第1オペランド側で指定します。第2オペランド側のテーブル内容は変更されません。

第2オペランドにTABLEと言う名前のテーブル領域を指定したとします。第1オペランドの主記憶のデータがx00の時、そのx00はTABLEの先頭からx00バイト離れた位置(TABLE+x00)のバイト内容に置き換わります。第1オペランドの主記憶のデータがx28の時、そのx28はTABLEの先頭からx28バイト離れた位置(TABLE+x28)のバイト内容に置き換わります。TR命令は文字コードの変換(例えばEBCDICからASCII)、2進→16進変換、非表示文字の変換(*2)などに使われます。

かなり手を抜いたテーブルですが、EBCDICのAからHまでの文字をASCIIコードに変換する例です。限られた範囲の文字を変換するだけなら256バイトを真面目に用意する必要はありません。OS/390アセンブラーハンドブックにも載ってます。

*2 例えば、メモリーダンプなどで文字ではないバイナリー値などを画面にそのまま表示すると意味不明の文字になったりするので、それを防ぐ為に空白やピリオドに置換えたりする。

TRT(Translate and Test)命令は、TRと似ていますが記憶域データの変換はされません。主に、文字列内の特定文字を探索するために使われます。第1オペランドで指定された主記憶の内容が、第2オペランドで指定されたテーブル(変換表)に従って探索されます。探索される長さは最大256バイトで、第1オペランド側で指定します。第1オペランド側の主記憶内容は、アーギュメントとも呼ばれます。第1および第2オペランド側のテーブル内容は変更されません。

第2オペランドにTABLEと言う名前のテーブル領域を指定したとします。テーブルはx00(NULL値)でクリアーします。空白文字を探したければTABLE+x40の位置に、文字Aを探したければTABLE+xC1の位置にそれぞれ0以外の値を設定します。これはファンクション・バイトと呼ばれます。複数の文字を探す場合は文字毎に値を変えればどの文字が見つかったかが簡単にわかります。TRT命令は読み込んだデータ内の区切り文字やコマンド・バイトなどを探す際などに使われます。
TRT命令は条件コードがセットされます。

0 … 全てのファンクションバイトが0(どの文字も見つからない)
1 … アーギュメントの途中で0でないファンクションバイトが検出された(文字がデータの途中に見つかった)
2 … アーギュメントの最後で0でないファンクションバイトが検出された(文字がデータの最後に見つかった)
3 … 使用されない

DOCSと言う名前の領域データを探索して、空白またはピリオドを探します。空白が見つかったら4、ピリオドなら8をファンクション・バイトとして設定しています。文字が見つかった場合(CC=1または2)、GR1には見つかったアドレスが、GR2の最下位バイトには対応するファンクション・バイト値が設定されます。GR2の上位3バイトは変更されません。文字が見つからなければ(CC=0)GR1とGR2は変更されません。

ED、EDMK

パック10進数を人間が見てわかるようにするために、UNPK命令によってゾーン10進数に変換する方法がありました。ここでは編集しながらゾーン10進数に変換してくれるED(Edit)およびEDMK(Edit Mark)命令を紹介します。
第2オペランドで指定された主記憶のパック10進数が、第2オペランドで指定された主記憶上の編集パターンに従って変換されます。変換される長さは、第1オペランド側である編集パターンのバイト数で指定します。変換元のパック10進数の内容は変更されません。

編集パターン
種類 備考
埋め文字 任意の文字 (一般的には空白)
数字選択文字 x20 ここに数字桁がゾーン変換されて入る。
有意開始文字 x21 この次のx20パターンから先行ゼロを出す。
通常は10の位をx21にすればよい。
その他 任意の文字 そのまま出力される。3桁ごとにカンマで区切ったり、小数点記号を置いたりする。
最終桁よりも右側に置かれた場合、元のパック10進数が正の場合、埋め文字に置き換わってしまう。それを防ぐには元の数値を一旦マイナス値に変えるか、第1オペランドの長さを短くしてED命令を実行するなどの工夫をする。
数字選択文字(有意開始文字も含む)は変換しようとするパック10進数の桁数分は必要である。ただし第2オペランドはバイト単位での指定なので、有効桁数が偶数の場合は符号ビットが加わると、最上位桁の左側に0がもう1桁追加される。なので偶数桁なら+1して次の奇数桁分の数字選択文字を用意する。数値が6桁なら最低7個のx20が必要である。数値が7桁ならそのまま7個のx20でよい。これに埋め文字とその他の文字を含めて必要な第1オペランドのバイト数を計算する。

ED命令は慣れるまでは期待した結果に編集されず、何度もトライ&エラーを繰り返す命令でもあります。命令リファレンスを見ても最初はよくわからないかも知れません。地道に試して慣れていって下さい。面倒な命令ですが、理解できるとアウトプットの編集が楽になります。
「000012345」と言う表示は間違いではありませんが、テストや自分だけで使うツールならともかく、正式品のプログラムなら「      12,345」と言う編集を面倒がらずに「やろうと思えばいつでもできる」ぐらいでいられた方がいいでしょう。暗記する必要などありません、リファレンス見ながらで十分です。

EDMK命令はED命令と同じですが、命令実行後にGR1に先頭の有効数字桁のアドレスが格納されます。(ただし格納されない例外条件もあるのでリファレンスマニュアルを参照)この機能によって先頭桁の前にマイナス記号や通貨記号などを付けることが容易になります。