02.1日付と時刻を得る(TIMEとTOD時計)

By kamii - Last updated: 日曜日, 11月 16, 2008

現在の日付と時刻を得る

TIMEマクロは現在の日付と時刻を求めます。DECが基本的な返答形式で、GR0に時刻をHHMMSSTH(時分秒)で返します。Tは1/10秒、Hは1/100秒を示します。他にもいくつかの形式がありますので必要に応じて使い分けます。日付はGR1にパック10進数0CYYDDDFで返ります。Cは世紀を表す数字で21世紀(2000?2099年)は1です。2008年なら1900年を起点に108年と見ればよいわけです。DDDは1月1日からの通算日付でジュリアンDATEと呼ばれます。Fはパック10進数の符号です。
日付/時刻を得るための基本的なAPIですが、日付を月日で得られないためプログラム側でYYDDD形式をYYMMDD形式に変換する必要があります。
MSPではDATE=YMD、VOS3ではDTYPE=YMDを指定すれば日付は CYYMMDDF の形式で返されます。


現在の日付と時刻を得る(新形式)

MVSでのみ有効な新形式のTIMEマクロです。現時刻と日付はレジスターではなく指定した16バイトの領域に符号無しパック10進数で返されます。時刻は1/100秒未満も返され、MIJUはそれぞれ1/1000、1/10000、1/100000、1/1000000秒に対応します。
マクロ名は同じTIMEですが、LINKAGEパラメーターで新旧形式が区別されます。


TOD時計

TOD時計(TOD Clock)はハードウェアのタイマー機構の1つです。実際の時刻を示し、すべてのCPUで共用されます。
TOD時計は64ビットの2進数カウンターで、ビット51が1マイクロ秒(1/1000000秒)を示します。1マイクロ秒毎にビット51に1が加算されます。1マイクロ秒未満のビット52?63はハードウェア性能によって分解能が変わるため時刻表示には使われません。しかしどのモデルであってもビット51は正しく1マイクロ秒毎に加算されるように設定されます。またビット31は1.048576秒毎に加算されます。ほぼ1秒です。そのためプログラムによってはTOD時計の先頭ワードのみを参照すれば、現在の日付と時刻を得るのに十分な精度が得られます。


0から51までのすべてのビットが1になるまで約143年掛かります。S/370アーキテクチャーではTODのスタート時刻は1900年1月1日午前0時O分0秒ちょうどになっています。まだ35年も先ですが、2043年にはTOD時計は一周して0に戻ってしまいます。

STCKはCPU命令です。現在のTOD時計の値をダブルワードの領域に格納します。STCKEはSTCKの拡張版で、ESA/390アーキテクチャー以降104ビットに拡張されたTOD時計の値を16バイトの領域に格納します。104ビットに拡張されたTOD時計も先頭のビット0?ビット51までは同じです。ビット51は1マイクロ秒を示します。将来の新CPUモデルでは2043年にTOD時計が一周してビット0が繰り上がると、STCKE命令で格納される先頭バイトに格納されるようになっています。プログラムは引き続き1900年を起点にする標準エポックで計時が可能になります。2043年以降も使われるプログラムを作るならSTCKE命令による拡張されたTOD時計のフォーマットと返答領域について知っておく必要があります。現時点で2043年以降のTOD時計について技術情報が公開されているのはIBM社のzアーキテクチャーだけです。

TOD時計の値はTIMEマクロでも得ることは可能ですが、わざわざAPIコールをする必要はありません。STCK命令で直接求められます。

TOD時計の値はそのままではただの2進数カウンターに過ぎないので、表示に使う場合はYYYYMMDDとHHMMSS形式に変換する必要があります。「OS/390アセンブラーハンドブック」でもTOD時計値の変換方法を解説していますが、現在では自作する必要はなく、クロック値を変換するサービスが用意されています。MVSではSTCKCONV、MSPではTIMECNVTの各マクロ命令で呼び出すことができます。VOS3には同等サービスがありません。MVSのSTCKCONVはSTCKVALに代えSTCKEVALパラメーターを使えば拡張TOD時計値をサポートしますので、2043年以降もプログラム修正の必要がありません。

TIMEマクロを使うかSTCK命令を使うかはプログラムデザイン次第ですが、日付や秒程度の分解能でかまわない時刻であればTIMEマクロが手軽です。デバッグや性能測定のためのログ情報などであれば1/100秒程度ではメインフレームの場合分解能が低すぎるので1/1000000秒まで測定できるTOD時計値が適しています。頻繁に現時刻を取得するならSTCK,STCKEはただの機械命令ですからパフォーマンス面では軍配が上がりますが、将来性を考えてSTCKEを使用すると格納領域長が16バイト必要になります。

Filed in .基礎編

レジスター番号の表記

By kamii - Last updated: 土曜日, 11月 15, 2008

多くのプログラマーはCPU命令のオペランドで指定するレジスター番号を、単に10進数ではなく Rn のようなシンボル名で指定します。これは即値としての数字と、レジスター番号を区別しやすくする工夫の1つです。たいていは頭にRを付けてR0,R1,R2,…,R9,R10,R11,…と書くことが多いです。10以降は16進数で、RA,RB,RC,RD,RE,RFと書くプログラマーもいます。私も後者です。理由は簡単で、0から15まで桁数が変わらないので、レジスター番号を変える場合(例えば LA R1,WORKAREA を、LA R15,WORKAREA に変える)コーディング時に挿入や削除をしなくて済むからです。

汎用レジスターだけを使うプログラムでは Rn あるいは Rx 表記でいいのですが、浮動小数点命令やアクセスレジスターを使うプログラムではレジスター種別をわかりやすくするため、汎用レジスターならGRnあるいはGPRn、浮動小数点レジスターはFPRn、アクセスレジスターはARnとしたりします。制御レジスターならCTLRnです。
またレジスターの使用目的でシンボル名を付けることもあります。GR13はレジスター保管域アドレスをポイントするのでRSAVEA、GR12をベースアドレスにするならRBASEのようにです。

レジスター番号は数字での直接指定、シンボル名での指定、どちらも自由です。単なる番号ではなく用途に合わせたシンボル名でもかまいません。ただし開発プロジェクトなどでレジスター表記の規約が定められているような場合はそれに従います。

Filed in S/370アセンブラー講座

01.1仮想記憶域を獲得する(GETMAINとFREEMAIN)

By kamii - Last updated: 金曜日, 11月 14, 2008

プログラムで使うデータ領域には、あらかじめその大きさを固定できないものもあります。例えばデータセットを読み込む処理で、レコードを読み込む領域の大きさを実際のデータセットのレコード長に合わせるような場合です。考え得る最大の長さでプログラム内に領域を用意するのも1つの方法です。しかしほとんどが100?200バイトの範囲で収まるが、たまに30000バイトなどと言う大きなものも取り扱う、と言ったケースでは、その「たまに」のケースのために無駄に大きい領域を定義しなければなりません。いくら今では仮想記憶はふんだんに使えると言っても、このようなプログラムは考え物です。

このような場合、プログラムは必要なときに、必要な量の仮想記憶域(ストレージ)をダイナミックに獲得して使うことが出来ます。これがMVSのGETMAINおよびFREEMAINサービスです。GETMAINはストレージを獲得し、FREEMAINはストレージを解放します。返却と言ってもいいでしょう。必要な時に必要な領域をOSから借りて、使い終わったら返す、のです。


72バイトのレジスター保管域をプログラムの外に確保する

最も基本的なGETMAINとFREEMAINマクロの例です。Rは16MBラインの下の領域(以下、24ビット域と記します)の無条件要求/解放を行います。LVパラメーターで獲得/解放する長さを指定します。FREEMAINマクロではAパラメーターで解放する領域のアドレスも指定します。
無条件要求なので獲得や解放に失敗するとプログラムはABENDします。例えば指定した領域が大きすぎて獲得できない、獲得されていない領域を解放しようとした、などです。GETMAINは成功するとGR1に獲得された領域の先頭アドレスを返します。GR0,GR14,GR15はマクロ内あるいはサービスルーチン側で作業用に使用されるため内容は破壊されます。GR2?13はそのままです。
このGETMAIN/FREEMAINも含め多くのサービスではGR0,1,14,15は作業レジスターとして使用されるか、サービスの処理結果(復帰コードや獲得された領域のアドレス等々)を通知する目的で使われます。このことは共通して覚えておくといいでしょう。GR14などはそのままの値が保持されるものもありますが、それはたまたまです。今は値がキープされていても、次のバージョンでは使われてしまうこともあり得ます。マニュアルにはその辺りもきちんと明記されていますから必ず確認してください。

このサンプルはリエントラント(再入可能)構造のプログラムを作る場合に使われるものです。リエントラントであることが要求されるプログラムでは自分のプログラム内にデータを書き込むことができません。そのためレジスター保管域も自分のプログラム内には定義できません。サンプルでは簡略化してありますが、実際には入口点ではGR0やGR1にパラメーターアドレスが入っていたり、復帰時にはGR15にリターンコードを返す必要があったりします。これらのリンケージレジスターGR0,1,14,15はマクロを発行されると内容が壊れてしまうため、必要に応じて別レジスターに一旦コピーしておくなどの工夫が必要です。どこにどういう命令を入れればよいかなどはご自身で考えてみて下さい。入口点でのGR13は呼び出し元レジスター保管域を指しますが、これは自分のプログラム内領域ではありませんから、リエントラントかどうかに関係なくそのまま使用できます。呼ばれたプログラムが最初に何をなすべきかはすでにご存じですね。それがわかれば壊されてしまうレジスターをどこでコピーすればよいかすぐにわかるでしょう。


OS出口ルーチンで使う作業用領域をプログラムの外に確保する

同じ無条件要求ですがRではなくRUパラメーターを指定しています。RU要求ではLOCパラメーターで獲得したいストレージの位置(16MBラインの上下)を指定できます。SPパラメーターはストレージを割り当てるサブプール番号です。(サブプールについてはここに簡単な説明があります「サブプールと記憶保護キー」)SP=230はPVTで、高位のアドレスから割り当てられる領域です。一般に使用されるSP=0は低位のアドレスから割り当てられます。OSの出口ルーチンでPVTを使う時SP=230はよく使われるサブプールです。ちなみに128以上のサブプールはAPF許可やスーパーバイザーモードのプログラムでなければ利用できません。

LOCパラメーターは省略すればプログラムのアドレスモードに合わせて適切な領域が獲得されます。24ビットモードなら16MB未満、31ビットモードなら16MB以上です。31ビットモードの時、マニュアルには16MBの上下いずれかからとありますが、現実には16MBの上から優先して割り振られます。仮に16MB以上の領域が一杯であれば、下からの割り振りになるのでしょうが、現実に16MB以上の領域が一杯になるような状況では別のエラーが先に起こるでしょう。
獲得するストレージ位置をOS任せにしてもいいのですが、APIの仕様でアドレスモードに関係なく16MB未満の領域を要求するものや、両方のアドレスモードを状況によって切り替えるようなプログラムでは、LOCパラメーターでストレージ位置を明示的に指定します。その方がソースやリストを見たときにわかりやすくなります。LOC=(BELOW,ANY)は仮想記憶上は16MB未満、実記憶は16MBの上下いずれでもよいことを示し、LOC=(ANY,ANY)は仮想記憶も実記憶も16MBの上下いずれでもよいことを示します。一般のプログラムは実記憶をアクセスすることはありませんから、24ビットモードのプログラムでは仮想記憶は16MB未満でなければなりませんが、リアルメモリーはどこであってもかまいません。MSPとVOS3ではBELOW/ANYのキーワードは同じですが構文に若干の違いがあるのでマニュアルを参照して下さい。なおz/OSではBELOW/ANYの代わりに現在では24/31と書くことが推奨されています。これは実記憶が64ビットに拡張されていることに関連します。

最初の例でリエントラントプログラムのレジスター保管域をプログラム外に獲得する方法を紹介しました。このサンプルではもう少し拡張して考えます。プログラムで書き込みを行うのはレジスター保管域だけとは限りません。その他にも処理に使うさまざまなデータをメモリーに書き込みます。S/370アーキテクチャーでは1つのベースレジスターでアドレスできるメモリーの大きさは4KBあります。レジスター保管域はGR13でポイントしますが、GR13をレジスター保管域だけの目的で使ってしまうと4KBアドレスできるのに先頭の72バイトしか使いません。もったいないことです。そこで先頭の72バイトをレジスター保管域にして、続く残りの領域をプログラムの作業用領域とすれば、最大4024バイトまでの領域を同じGR13でアドレスすることができます。GR13はレジスター保管域のアドレスを格納するものでそれ以外の目的に使ってはならないと、頑なに信じていませんか?。レジスター保管域の後ろにプログラムの作業領域を置いても、GR13が72バイトのレジスター保管域のアドレスを示すことに変りありません。特にリエントラントプログラムではレジスター保管域はプログラム外に置くのが鉄則ですから、どうせならその後ろの領域も積極的に活用しましょう。


31ビット領域にバッファ用ストレージを獲得する

RCは条件付き要求です。条件付き要求とは指定した長さのストレージが獲得できない時や指定した領域が解放できない時に復帰コードで通知されます。プログラムは復帰コードを判定することで、要求したストレージが獲得できたか、解放できたか、がわかります。復帰コードはGR15で通知され、GETMAIN/FREEMAIN共に成功すれば0が返ります。0以外の時は要求が失敗したことを示します。

BNDRYパラメーターは割り当てる領域の先頭アドレスをどのように境界合わせするかの選択です。DBLWD(8バイトバウンダリー)とPAGE(4KBバウンダリー)があり、デフォルトはDBLWDです。

サンプルではFREEMAINにはRUを使っています。もちろんRCでもかまいません。ただFREEMAINの場合、指定した領域が返却できないのはシステムやJCL(REGIONサイズ)の問題よりは領域アドレスやサブプール番号を誤って指定していたりするケースがほとんどですから、基本的にはプログラムのバグです。RU要求ならばこのような時必ずABENDしますから、それでバグがあることがわかります。ABENDを嫌ってRC要求にしても、復帰コードを判定していないなど、きちんとエラー処理をしないのならば、結果として領域の解放漏れを見逃してしまうことにもなります。


サブプールの解放

一般のプログラムではあまり使われませんが、FREEMAINにはサブプールの解放と言う機能もあります。特定のサブプールに属するGETMAIN済みストレージをまとめて解放します。例えばサブルーチンの処理結果を返す領域をサブルーチン側で用意し、解放はメインルーチン側で行うような場合、アドレスや長さを管理しなくともサブプール単位でまとめて解放することができます。
サブプール単位の解放はTPモニターなどアプリケーションプログラムを制御する側にとっては便利な機能です。


STORAGEマクロ(MVSのみ)

MVSではGETMAIN/FREEMAINに代わり、OBTAINマクロを使用することもできます。GETMAIN/FREEMAINはSVC命令でAPIが呼び出されますが、OBTAINではPC命令によるクロスメモリー機能が使われます。一般のプログラムではGETMAIN/FREEMAINでかまいませんが、SVC命令を発行できないモードで走行中のプログラムやアクセスレジスターモードで走行中のプログラムではOBTAINマクロを使う必要があります。SVC命令を発行できないモードで走行中のプログラム(SRBルーチンやタイプ1SVCルーチンなど)では従来はブランチ命令によって、OSのGETMAIN/FREEMAINルーチンを直接呼び出す必要がありましたが、ローカルロックの保持など、OSのSVCハンドラー並みの環境設定を行う必要があります。STORAGEマクロによる方法はコーディングも容易で、どのようなモードのプログラムでも共通に利用できるストレージサービスです。


ワンポイント知識

Filed in .基礎編

.APIとマクロ命令

By kamii - Last updated: 水曜日, 11月 12, 2008

実用的なプログラムやシステムプログラム、OSの出口ルーチンなどを作っていく場合、避けて通れないのがMVSの各種プログラミングサービスの利用です。プログラムに必要と思われるさまざまな機能がAPIで提供されています。プログラムから直接分岐命令で呼ばれるもの、SVC命令で呼び出すもの、新しいAPIはPC命令でクロスメモリーサービスを使った呼び出しなども行われます。いずれも一般のプログラムはOSが提供するマクロ命令を使って呼び出しますので、細かな命令を最初は意識する必要はありません。
APIマクロはSYS1.MACLIBもしくはSYS1.MODGENに格納されています。一般のプログラムで使用するものはほぼSYS1.MACLIBで提供されます。

マクロには実際にAPIを呼び出すためのパラメーター設定や呼び出し命令などを展開する「実行マクロ」と、APIのパラメーター領域やOSのコントロールブロックをラベル名で参照するための「マッピングマクロ」に大別されます。

マクロによっても多少異なりますが、実行マクロには標準・リスト・実行の3形式があります。標準形式はサービスの呼び出し命令列とパラメーターの定義が同時に行われるもので、一般的に使われるものです。リスト形式はパラメーター定義だけを行うもので、データ領域に記述します。実行形式はサービスの呼び出し部分だけが展開され、リスト形式で定義したパラメーターと組み合わせて利用します。リスト+実行形式の組み合わせは、プログラム内に書き込みが出来ないリエントラント構造(再入可能)のプログラムでマクロを使う場合や、プログラムの外にある領域をパラメーターに指定したい場合に利用します。最初のうちは標準形式のマクロでいろいろなAPIの基本的な使い方を覚えてから、必要に応じてリスト+実行形式でのコーディング・テクニックを身につけるといいでしょう。

マッピングマクロは基本的に領域のDSECTを展開するものです。
身近なものにアクセス法のDCBをマッピングする「DCBD」と言うマクロがあります。DCBDを使うと、プログラム内に定義したDCBの各フィールドを名前で参照できるようになります。DCBマクロ自体に定義したパラメーターを変更したり、プログラムの処理によって値が変わるようなパラメーターを設定する際に利用します。
OSの出口ルーチンなどを作る場合は、出口ルーチンに渡されたパラメーター領域や処理に必要なOSのコントロールブロックを参照するために利用します。
DSECTについては「OS/390アセンブラーハンドブック」に説明があります。


DCBDマクロによるDCBのマッピング・サンプル

DEVDパラメーターにDA(DASDの意味)、DSORGパラメーターにQS(QSAM)を指定した例です。
DD名などを固定せずに、パラメーターで指定できるようにするプログラムではOPENまでにDD名をDCBに直接セットする必要があります。このような時マニュアルなどを見てDCBの各フィールドの位置がわかったとしても、MVC SYSPRINT+40(8),PARMDDN のように書くのはよくありません。OSのコントロールブロック類は必ず対応するマッピングマクロを使い、サンプルのようにフィールド名で参照するようにします。MVSではAPIに直接関連するフィールドの名前や位置が変更されることはありませんが、ユーザー独自のパラメーター領域などでは、将来変更などがされた際にアセンブルし直すだけで対応できるようになります。また SYSPRINT+40 よりは DCBDDNAM の方がDCBのどこをいじっているか、直感でわかります。また40を間違えて50としてしまってもアセンブルは通りますから、後でデバッグする羽目になります。フィールド名を間違えればアセンブルでエラーになりますからそこで間違いに気づくことができます。

Filed in APIで学ぶMVSの機能

IPCS以外のVTAMトレースの解析プログラム(TAP)

By kamii - Last updated: 水曜日, 11月 12, 2008

VTAMのトレースデータはIPCSでもフォーマットできますが、NCPが提供するTAP(Trace Analysis Program)と言うプログラムを使うと、SNAのプロトコルをより詳細に編集してフォーマットすることもできます。今では使い機会も少ないでしょうが、いざと言うときのためにサンプルJCLを残します。MVS専用です。
だいぶ古いJCLなので最新のTAPでは若干変わっているかも知れません。以前はNCPのDIAGNOSIS GUIDEマニュアルに記載されていましたが今でも入手できるかはわかりません。
※2009/11月追記:最新のz/OS V1R11で、「Communications Server, ACF/TAP Trace Analysis Handbook」としてCSのマニュアル・ライブラリーに追加されていました。

ACFTAP実行JCLサンプル

Filed in ありがたいサンプルJCL

10.SYSINとSYSOUTのI/O

By kamii - Last updated: 水曜日, 11月 12, 2008

CPU命令だけを使っていても、あまり面白くないし、実用的ではありませんね。それに「アセンブラーではI/Oをどうやって処理するのだろう?」と言うことにも触れなければなりません。誰かに呼ばれるだけのサブルーチンやOSの出口ルーチン(これもOSから呼ばれるサブルーチン)でなければ、プログラムにI/Oはつきものです。

プログラムの基本的なINPUTとOUTPUTはこう定義することができます。ここでは実際にこのように定義されたDD文に基づく入出力先に対するI/Oのやり方を覚えて下さい。基本的なI/Oはアセンブラーでも大して難しくありません。コードを書くことだけに関して言えばCOBOLと大差ないのです。


QSAM(待機順アクセス法)

QSAMは順次データセットをアクセスするためのMVSのAPIです。正確にはDFSMS/DFPと言うデータ管理コンポーネントが提供するAPIです。CPUには命令としてのI/O命令(SSCH:Start Sub CHannel)もあります。しかしこのI/O命令を使って一般のプログラムが入出力を行うことはありません。たとえ命令解説書を読んで理解したとしても決してやってはいけません。MVSではI/OはすべてOS自身が一元管理します。I/Oに関するOSの機能はいくつかの階層に分かれていますが、一般のプログラムは、DFPと言うもっともアプリケーション寄りのコンポーネントにQSAMなどのAPIを使ってI/Oを要求します。
このAPIを呼び出すために使われるのがマクロ命令です。マクロはアセンブラーの機能でいくつかの命令をまとめたもので、定型的な処理などを行うために使われます。マクロを使わず直接CPU命令でDFPのAPIルーチンを呼び出すこともできますが、一般的ではありません。最初は(ほとんどのケースで最後まで)マクロ命令を使って基本的なアクセスの仕方を覚えましょう。

QSAMではデータセットをレコード単位にアクセスできます。上のJCLではDD文でSYSINとSYSOUTを定義してますが、データセットそのものを定義してもかまいません。QSAMを使ってアクセスする限り、DD文から先の実際のデバイスに関してはプログラムで意識する必要がありません。
なお、ここで解説するQSAM APIは従来からある24ビットモードのAPIです。31ビットモード・サポートのAPIについては解説しません。24ビットモードAPIはMVS、MSPおよびVOS3それぞれ互換があります。


簡単ですがQSAMによるデータセット(SYSIN,SYSOUT)のアクセス方法を紹介しました。アセンブラービギナーがファイルのI/Oのために最初に使うのがQSAMです。理由はビギナーでもわかりやすい簡単なインターフェースだからです。そのためQSAMは初心者向きのEASYなアクセス法だと誤解されがちですが、決してそれだけではありません。QSAMはアクセス法の中でも高いI/Oパフォーマンスを持ちます。後ろへ戻ったり、レコードをスキップして読んだりなどの細かな制御はできませんが、順次にアクセスすれば良いのなら、初心者でもそれなりの高速で動くI/Oプログラムが作れます。中級レベルのスキルのプログラマーが作ったBSAMやEXCP(ブロック単位アクセス)のプログラムより、初心者が作ったQSAMのプログラム(レコード単位アクセス)の方が恐らく性能が高いでしょう。QSAMは手軽に利用できますが、実際は奥深いアクセス法でもあります。

ざっとまとめるとこんな感じのサンプルになります。処理したいデータをSYSINから読んで、実行結果をSYSPRINT(SYSOUT)に出すものです。
COBOLと比べてもOPENはOPEN、CLOSEはCLOSE、READがGET、WRITEがPUTとソースを書く手間は高級言語とほとんど変わりません。I/Oそのもののコードを書くこと自体はそれほどむずかしくないでしょう。

Filed in S/370アセンブラー講座

11.入門編番外(おわりに)

By kamii - Last updated: 火曜日, 11月 11, 2008

アセンブラー基礎講座・入門編のおわりに番外編として実用的なヒントなどを少し。ここまでわかれば「OS/390アセンブラーハンドブック」を読んでもきっとその内容が理解できることと思います。
ハンドブックで述べていますが、アセンブラーのプログラムは「大部分が定石ともいうべきコードの書き方で、大半が構成される」と言えます。ここまでに解説したことはその定石コードを理解するために必要な前提知識でした。OS/390アセンブラーハンドブックには実際の現場で役立ついろいろなヒントが載っていますから、是非活用して下さい。
また実用品のプログラムを書くためには、MVS(MSP、VOS3)のAPIを使うことを避けて通れません。CPU命令だけの実用品プログラムはOS自身の割り込み処理ルーチンでもない限り、我々のレベルで書くことはまずありません。APIについては別のカテゴリーで新たな講座を始めようと思います。


レジスターに1を足す、1を引く

レジスターの値に固定値を加算、減算するには加減算命令の使用が思い浮かびます。しかし内容によっては加減算命令でなくても行う方法があります。

このテクニックはS/370アセンブラー言語では古くから使われています。いずれも命令の動作を応用した「へぇー、そうなんだ」と言うものの代表です。
覚えるとアセンブラーがちょっとわかってきた気がする、小さな幸せですが、実戦では多用されています。
(LA命令は24ビットモードで最大16MB-1、31ビットモードで最大2GB-1までしか扱えないのと減算は出来ないことに注意)
他にもいくつかの「へぇー、そうなんだ」が「OS/390アセンブラーハンドブック」にも載ってます。

ESA/390で追加された新しい命令です。BCTR命令では1しか引けませんが、これなら-32768から+32767の加減算が命令とは別のハーフワード数の定義なしでできます。講座ではS/370アーキテクチャーでの基本的な(伝統的な)命令しか解説してませんが、慣れたら命令リファレンスを見て便利そうな命令は積極的にトライして見て下さい。ただし富士通、日立でも動かすプログラムを作る場合は注意して下さい。特にzアーキテクチャーで追加されたような最新の命令セットはどこまで互換かは動かしてみないとわかりません。


文字列転送

これもESA/390での新しい命令で、MVSTを紹介します。C言語で言うstrcpyです。MVCやMVCLと違い転送する長さを指定しません。文字列の終端文字(NULLターミネート)で長さが認識されます。終端文字も含めて文字列が転送されます。

比較ならCLST命令があります。
ESA/390以降ではUNIXシステムサービスやLINUX自身をサポートするようになったため、LINUXの移植を意識した?と思われる命令がいろいろと追加で実装されています。それらはすべてが公開されていないようですが、一部はアーキテクチャー解説書に載っています。MVSTもCLSTも公開命令です。


わかりやすいロジックを心がけましょう

アセンブラー自体がわかりにくい、と言われてしまえばそれまでですが、わかりやすいプログラムがいちばんです。せっかくアセンブラーを使うのですからクールなロジックを書くのもいいのですが、他人が見てわかるプログラムでないといけません。多少冗長なロジックでも、メモリーをそれなりに使っても、CPUを少し余計に使っても、普通の人が見てわかるロジックにすることが実用品のプログラムでは大切です。LA命令で加算したり、BCTR命令で1を引いたりするのは、命令の動きや機能がわかっていれば簡単に解読できるので、そういうものはいいと考えます。ただしロジックを解読するのに複雑な数学の知識を必要としたり、あまりにもトリッキーなコードは考え物です。書いた人自身が生涯を掛けてメンテナンスするならともかく、ほとんどのプログラムがそうではありません。特にアセンブラー言語でのコーディングが要求されるプログラムはOSの出口ルーチンなどシステムプログラムの部類に入るものがほとんどです。そうでなくてもOSのコントロールブロックを参照したりするため、CPU命令そのものよりはOSの内部動作や構造に関する知識が求められます。命令自身で処理する部分はなるべくシンプルなコードにして保守する人の負担を減らした方がいいと考えます。いくらコメントをきちんとつけても難解なものは難解です。
このあたりはプログラマーのプライドにも関わってくるので賛否両論ありましょうが、自分自身の経験から言えば、やはり「シンプル・イズ・ザ・ベスト」です。そしてこの種の文句は自分の作ったプログラムに対してではなく、他人が作ったプログラムに対して生まれます。そもそも自分が書いたコードですら何年か経って見返した時、「これって何をやってたんだ?、何のために必要だったんだっけ?」「ひどいなぁ」と思うのですから、人が書いたコードなら尚更です。

実用ではたぶん必要としませんが、アセンブラーっぽい例題でひとつ例を挙げます。「ビットの数を数え上げる」

最初の方法はビットを左へ1つずつずらして、溢れたビットを足し算しています。溢れたビットが1なら+1になり、0なら0を足しても変わりません。命令の動きを知っていれば比較的容易に理解できます。
2番目の方法は雑誌に載っていたインテルのx86アセンブラーのサンプルをS/370用に直したものです。32ビットを4ビットずつ8つのブロックに分けて、4ビット単位に1の個数を求め、その考えを32ビットに拡張するものだそうです。4ビットをabcdと4つに並べた時、この4ビットの値は「8a+4b+2c+1d」となるそうです。これから「7a+3b+1c」を引くと、その4ビット中で1になっているビットの個数が求められる、と言うものです。
このページを書いた僕自身がよくわかっていないので、サンプルのコメントもただの命令動作の説明でしか書けませんでした。ちなみにその雑誌の記事では、上記2通りが紹介されていて、アセンブラーならではのコードとして紹介されていたのが「8a+4b+2c+1d」から答えを求めるものでした。その理由は実行する命令数が少ないからです。1番目の方法はソースコードの行数は少ないですがループするので、実際に実行される命令数は131命令になります。2番目の方法はソースコードの行数は多いですがループしないので、実際に実行される命令数は25命令で済みます。CPUの使用量、実行に掛かる時間で言えば、当然2番目の方法に軍配が上がります。

しかし実際の現場仕事としてのプログラムならどうでしょうか?少なくとも私にはよくわかりませんでした。今もわかってませんし突き詰めようとも思ってません。サンプル通りに書いたから動きますが、考え方だけ言われてゼロからコードを書けとなったら、きっとお手上げです。このトピックのネタにしたのも、アセンブラー講座の題材を探すためにめくってた昔の雑誌に載っていたのを見て、「これって(アセンブラーらしいプログラムだよと)言いたいことはわかるけど、こんなのみんなパッと見てわかんのかぁ」と思ったからです。

数学がわかっている人には常識かも知れないし、簡単じゃんこんなの、となるかも知れませんが、実際の現場には数学を知ってる人ばかりとは思えません。たった25命令で処理できるクールなロジックなのは認めますが、普通の人にこんなのわかるかぁ?って思います。CPU使用量は約5倍ですけど、たかが131命令。実際のところ大した違いではありません。数値計算や科学技術計算のプログラムなら別ですが、たいていのシステムプログラムではここまでこだわる必要性はないと考えます。それよりは後で別の人が、普通の人が見てもわかるプログラム、として書く方がいいと思います。アセンブラーらしいとか、CPUの機能を知り尽くしたようなコードもありですが、ソートやデータベースの検索エンジンなど、性能命のソフトはともかく、実用で作るプログラムはわかりやすくないと困ります。そうでなくてもアセンブラーなんてわかんないから困るって言うのが現実ですから。

Filed in S/370アセンブラー講座

09.ビット操作と論理演算

By kamii - Last updated: 火曜日, 11月 11, 2008

CPU命令の解説の最後はビット操作と論理演算(AND,OR,XOR)です。ビット操作はとてもアセンブラーっぽいプログラミング技法です。1バイトあれば8つの情報を持てますし、使いこなせるとプログラムはとってもスマートになります。


ビット操作の命令


論理演算命令(And,Or,Exclusive Or)

Filed in S/370アセンブラー講座

08.比較と分岐・ループ、サブルーチンの呼び出し

By kamii - Last updated: 月曜日, 11月 10, 2008

分岐とループは実用的なプログラムを作る上での基本でもあります。関連する機能でもある比較と併せてS/370アーキテクチャーにおける、分岐・ループ、サブルーチンの呼び出しなどについて解説します。基本の分岐命令(BC命令)については「四則演算の基本と条件分岐」を見て下さい。


整数比較命令(Compare)


文字(文字列)比較命令(Compare Character)

これらの他にMVCL命令の比較版であるCLCL命令もあります。使用する機会は少ないと考えるので講座では解説しません。必要ならば命令リファレンスマニュアルを参照して下さい。


ループ制御に使う分岐命令


サブルーチンの呼び出し

サブルーチンの呼び出しも分岐命令です。S/370(XA)命令セットではアドレスモードによって複数の命令を使い分けられるようになっています。

Filed in S/370アセンブラー講座

06.REXX データの分解

By takao - Last updated: 月曜日, 11月 10, 2008
いきなり「データの分解」が現れたのはREXXにおいて、他のプログラムを呼び出したりする際の引数、戻り値、入力を処理する、などにおけるキー”PARSE”がわからないと、やる気がなくなってしまうからです。PARSEはデータの分解を行う命令です。

シンタックス


形式は次のとおり

PARSE  [UPPER]  ソース  テンプレート

ソースから供給されたデータを、テンプレートに分解します。[UPPER]が入っていたら全部、大文字に変換します。
ソースに許されているキーワードは次のとおり
です。
次は、テンプレートに使われる表現です。
これだけじゃ、なんのことやらほとんどわからんと思います。例をあげますので、ゆっくり読んでください。

パターン


例1.
/* REXX */
DATA = “Hello, the world!”
PARSE VAR DATA w1 w2 w3
SAY w1 “/” w2 “/” w3
結果:
Hello, / the / world!

ブランクで文字を区切っていくというのが基本動作です。

例2.
/* REXX */
DATA = “Hello, the world!”
PARSE VAR DATA w1 . w3
SAY w1 “/” w2 “/” w3
結果:
Hello, / W2 / world!

.(ピリオド)プレースホールダはこのように、その場所の文字を無視させます。その結果、W2にはなにも割り当てられませんでした。 これで欲しい文字列だけを変数に割り当てられます。

例3.
/* REXX */
DATA = “Hello, the world!”
PARSE VAR DATA w1 “,” w2 w3
SAY w1 “/” w2 “/” w3
結果:
Hello / the world!

注意してみてください。Helloから ,(カンマ)がなくなりました。場所あわせのため,を使ったため、W1にはHelloだけが割り当てられたのです。
データを変えてみましょう。
例4.
DATA = “YAMADA,TAROU,34,TOKYO”
PARSE VAR DATA SEI “,” MEI “,” . “,” BASHO
SAY SEI “/” MEI “/” BASHO
結果:
YAMADA / TAROU / TOKYO

カンマ区切りのcsvから必要なデータだけを抜き出すといったことが簡単にできます。

データがカンマなどで区切られていない代わりに、場所が決まっているという場合の取り出し方は次のとおりです。コンピュータの出力を処理する場合などは、むしろこのほうが多いかも知れません。

/*——123456789012345678901234567890123456789 */
DATA = “00000001 3F563F56 CCF53422 33652100 …..”

このデータで、頭から8文字目まではアドレス、欲しいデータは23文字目から4文字だとします。(上の3422のところ)
書き方はいろいろ考えられます。
素直に1文字目から長さ8をアドレス、23文字目から長さ4が値とすると、
PARSE VAR DATA 1 ADDRESS +8 . 23 VAL +4 .
となります。

さて、今まで”"や数字で指定したところをプログラムの変数で処理できないでしょうか?そのためには、次のようにします。

DATA = “00000001 3F563F56 CCF53422 33652100 …..”
OFFSET = 23
PARSE VAR DATA 1 ADDRESS +8 . =(OFFSET) VAL +4 .

変数で位置を指定する場合は、絶対位置なら=、相対位置ならば+を明示してください。

ソース


変数を入力とする場合は、上のパターンの学習でさんざんやりましたから、いいでしょう。他の入力をご紹介します。

端末
PARSE PULL INPUT
SAY INPUT
PULLは、スタックのところで学んだように、スタックになにもなければ端末の入力待ちとなります。
その結果、エンターキーを押すまでの入力を得ることができます。

ARG
サブルーチンの引数を得る場合に使います。

CALL add 1,2
SAY RESULT
EXIT

add: PROCEDURE
PARSE ARG a,b
RETURN a + b


あとのソースはあまり使わないので、必要に応じてマニュアルを見てください。
Filed in REXX入門