10.SYSINとSYSOUTのI/O
CPU命令だけを使っていても、あまり面白くないし、実用的ではありませんね。それに「アセンブラーではI/Oをどうやって処理するのだろう?」と言うことにも触れなければなりません。誰かに呼ばれるだけのサブルーチンやOSの出口ルーチン(これもOSから呼ばれるサブルーチン)でなければ、プログラムにI/Oはつきものです。
//STEP1 EXEC PGM=MYPROG //SYSPRINT DD SYSOUT=* //SYSIN DD * ABCDE FGHIJ A=ABC,B=123 //
プログラムの基本的な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それぞれ互換があります。
DCBマクロ(Data Control Block)
データセット(SYSIN、SYSOUTも含めここではデータセットと言います)をアクセスするために必ず必要になるのがDCBです。DCBはプログラムがDFPに示すパラメーターでもあります。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- SYSIN DCB DDNAME=SYSIN, + DSORG=PS,MACRF=GM|GL,EODAD=ENDSYSIN, + RECFM=FB,LRECL=80 SYSPRINT DCB DDNAME=SYSPRINT, + DSORG=PS,MACRF=PM, + RECFM=FB|FBA,LRECL=132|133
DCBはデータなので、プログラム内でもデータ領域に置きます。
最低限必要なのはDSORGとMACRFです。その他のパラメーターは任意ですが、DDNAMEはここで指定します。省略できますがDDNAMEはOPENするまでにはプログラムで直接セットしなければなりません。QSAMを理解し覚えるまではあまり応用編にとらわれず基本的な使い方を通してしくみを理解する方がいいでしょう。
まず入力となるSYSINのDCBからです。ラベル名は任意です。ここではDD名をラベルとしています。DDNAMEパラメーターでDD名を指定します。DSORGパラメーターにはPSと指定します。DSN=dsname(member)で区分データセットのメンバーをアクセスする場合でもPSです。MACRFはGMもしくはGLです。GMは移動モードで読み込んだレコードのデータをプログラム内の指定された領域にコピーします。始めはGMがわかりやすいでしょう。GLは位置づけモードで読み込んだレコードの先頭アドレスをGR1に返します。(QSAM APIのバッファ内にあるレコードをそのままアクセスする)GLの利点はプログラム内にレコードの読み込み領域を必要としない点です。READ OnlyのデータならGLが便利です。なお先頭のGはGetの意味でこのデータセットから読み込むことを示します。
EODADパラメーターはINPUTアクセスでは必要なパラメーターです。最後のレコードを読んだ後、さらに次を読もうとすると、ここで指定したアドレスに飛んできます。そこでEOD(End Of Dataset)状態になったことがわかります。EODADを忘れると最終レコードの次を読もうとするとABENDさせられます。
RECFMとLRECLは任意です。JCLにDCBパラメーターがありますが、これが同じものです。プログラムで指定するかJCLで指定するかです。ユーティリティのような汎用的なプログラムではこれらのパラメーターは指定しないのが普通です。それはどのようなRECFM、LRECLのデータセットでも受け入れるためです。しかしアプリケーションであれば固定長レコードしか扱わない、レコード長は80でなければならない、などプログラム側の都合を優先する場合は指定した方がいいでしょう。RECFM=FBなのに可変長レコードのデータセットを指定したり、LRECL=80なのに100バイトのレコード長のデータセットを指定したりすれば、OPENした時にABENDします。そうしておかないと属性が合わないデータセットを指定しても通ってしまうので、プログラム内での処理がおかしくなったり、プログラムやデータ領域を破壊してしまったりします。なおBLKSIZEは基本的に指定すべきではありません。レコード単位のアクセスですからブロック長を固定するメリットはありません。
次に出力であるSYSPRINTのDCBです。MACRFでPMを指定します。PはPutで出力、Mは移動モードのMです。位置づけモードもありますが、最初はわかりにくいので解説しません。PMはGMの逆で、プログラム内に用意したレコード領域の内容をそのままデータセットにレコードとして書き出します。RECFMとLRECLは出力時にはプログラム側で指定するのが一般的です。データセット側の属性にプログラムが合わせることもありますが一般的ではありません。特に実行結果の要約など、印刷リストの場合はプログラム側で横幅の桁数などを決めるのが普通です。ブロックサイズは指定しなければMVS(MSP)が最適なサイズを設定します(SYSOUTでない限り、VOS3ではブロックサイズは指定する必要があります)。RECFM=FBAはANSI印刷制御文字付きレコードを示します。これは出力レコードの先頭にプリンターの制御文字を付けるものです。改ページや改行などがあります。
OPENマクロ
OPENマクロでデータセットをオープンします。アクセスに先立ってあらかじめオープンしておかなければなりません。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- OPEN (SYSIN,INPUT) OPEN SYSIN DATASET LTR R15,R15 SUCCESS ? BNZ ... NO, OPEN (SYSPRINT,OUTPUT) OPEN SYSPRINT DATASET OPEN (SYSIN,(INPUT), OPEN SYSIN AND SYSPRINT + SYSPRINT,(OUTPUT))
第1パラメーターでDCBアドレス、第2パラメーターでモードを指定します。入力ならINPUT、出力ならOUTPUTです。
OPENでは復帰コード(成功すればRC=0)がGR15に返りますが、実際には対応するDD文の定義忘れでRC=8が返ります。それ以外のエラーは特別なエラーリカバリー処理を組み込まない限り、復帰コードでは返されず、プログラムはABENDさせられます。
CLOSEマクロ
OPENマクロでオープンしたデータセットをクローズします。データセットのアクセスが終了したら、あるいはプログラム終了時にはCLOSEマクロを使ってデータセットをクローズする必要があります。万一クローズし忘れても、途中でABENDなどしても、MVSはプログラム終了時に代行してクローズ処理を行います。どうせOSがやってくれるからと言ってあえてCLOSEを発行しないプログラムを作るのはよくありません。取ったら返す、起動したら止める、上げたら降ろす、後始末は必ず行います。アセンブラーで作るシステムプログラムの場合、リソースの返却漏れ、解放忘れは致命的な障害を引き起こす原因になります。やさしいプログラムや勉強で作るプログラムのうちから、きちんと後始末を付ける癖をつけましょう。後始末処理はクリーンアップ(Clean Up)処理とも呼ばれます。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- CLOSE (SYSIN) CLOSE SYSIN DATASET CLOSE (SYSPRINT) CLOSE SYSPRINT DATASET CLOSE (SYSIN,,SYSPRINT) CLOSE SYSIN AND SYSPRINT
第1パラメーターでDCBアドレスを指定します。第2パラメーターもありますが通常は省略します。複数のDCBを指定する場合、カンマ記号のみ指定して、DCB1,,DCB2,,DCB3,,…のように記述します。
CLOSEでも復帰コード(成功すればRC=0)がGR15に返りますが、実際には対応する正しくないDCBの指定や、未オープンのデータセットのCLOSEでRC=4が返ります。CLOSE処理に伴うI/O動作でのエラーなどは特別なエラーリカバリー処理を組み込まない限り、復帰コードでは返されず、プログラムはABENDさせられます。
QSAMでレコードを書き込んだ場合、QSAM側のバッファに残っているレコードデータがあればCLOSE処理の中でデータセットに書き込まれます。
GETマクロ
データセットからレコードを読み込みます。GMモードの時は指定した領域にレコード長の分だけ読み込まれます。GLモードの時は読み込んだレコードの先頭アドレスがGR1で通知されます。EOD状態の時はDCBのEODADパラメーターで指定したアドレスへ制御が渡ります。可変長レコードの場合、レコードの先頭にはRDWが付加されていますので、それを見て実際のレコード長を求めます。固定長および不定長レコードではDCB内のDCBLRECLフィールドに読んだレコードの長さが格納されます。固定長であればプログラム側でもレコード長がわかっていますから普通のプログラムであればDCBをいちいち参照する必要はありません。DCB内のフィールドの参照については機会を改めて解説します。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- GET SYSIN,PARMCARD READ NEXT RECORD TO OUR AREA : : ENDSYSIN DS 0H CLOSE (SYSIN) CLOSE SYSIN DATASET : : PARMCARD DC CL80' ' PARAMETER CARD READ AREA GET SYSIN (MACRF=GL) LTR R1,R1 EOD ? BZ CLOSE YES, DO CLOSE PROCESSING CLI 0(R1),C'*' COMMENT CARD ? BE SKIP YES, IGNORE IT : : ENDSYSIN DS 0H SLR R1,R1 INDICATES E.O.D OCCURED BR R14 RETURN TO PROGRAM
第1パラメーターでDCBアドレス、第2パラメーターでレコードを読み込む領域のアドレスを指定します。GLモードの場合はDCBアドレスだけを指定します。
GR13は72バイトのレジスター退避領域をポイントしていなければなりません。GETマクロは分岐命令で呼び出されるサブルーチンAPIです。QSAMルーチン側でレジスターの退避が行われます。GETマクロから戻ってきた時、GR2からGR13はマクロ発行前と同じです。復帰コードはありません。
EODADルーチンに入ってきた時、GR2からGR13はGETマクロ発行前と同じです。GR14はGETマクロの次の命令を指しています。2番目のサンプルではEODADルーチンはGR1のレコードアドレスを0クリアーしてGETマクロの次の命令に飛んでいます。こうすることでメインの処理でEODを判定できます。GMモードの時はGR1に変えて、別の領域にEODフラグなどを用意して、それをON/OFFしてもいいでしょう。入力データセットを読み終えたら処理終了となるようなプログラムなら1番目のサンプルのようにEODADルーチンから終了処理に入ってしまってもかまいません。
PUTマクロ
データセットにレコードを書き込みます。PMモードの時は指定した領域にあるデータがレコード長の分だけ書き込まれます。可変長レコードの場合、レコードの先頭にはRDWを付加しなければなりません。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- PUT SYSPRINT,PRNTREC WRITE PRINT OUT RECORD : : PRNTREC DC CL133' ' PRINT DATA RECORD ORG PRNTDATA ANSICODE DC CL1' ' ANSI CODE SPACE1 EQU C' ' 1LINE SPACE SPACE2 EQU C'0' 2LINE SPACE SPACE3 EQU C'-' 3LINE SPACE SPACE0 EQU C'+' 0LINE SPACE NEWPAGE EQU C'1' NEW PAGE PRTDATA DC CL132' ' PRINT OUT DATA ORG ,
第1パラメーターでDCBアドレス、第2パラメーターで書き込むレコードが格納された領域のアドレスを指定します。
GR13は72バイトのレジスター退避領域をポイントしていなければなりません。PUTマクロから戻ってきた時、GR2からGR13はマクロ発行前と同じです。復帰コードはありません。
RECFM=FBAまたはVBAの時、レコードの先頭にはANSI制御文字(内容はサンプル参照)を置きます。特別な制御をせずに行単位にそのまま印刷する場合は、レコードの先頭に空白文字を置けばいいでしょう。VBAの場合はRDWに続いてANSI制御文字となります。ANSI制御文字はJES2ライターから印刷する場合に使われます。VTAMやTCP/IP(TN3270)などで接続されたプリンターに出力する場合、ANSI制御文字がプリンター自身の制御文字に変換されるかどうかは使っているソフトウェアなどの仕様によります。
簡単ですがQSAMによるデータセット(SYSIN,SYSOUT)のアクセス方法を紹介しました。アセンブラービギナーがファイルのI/Oのために最初に使うのがQSAMです。理由はビギナーでもわかりやすい簡単なインターフェースだからです。そのためQSAMは初心者向きのEASYなアクセス法だと誤解されがちですが、決してそれだけではありません。QSAMはアクセス法の中でも高いI/Oパフォーマンスを持ちます。後ろへ戻ったり、レコードをスキップして読んだりなどの細かな制御はできませんが、順次にアクセスすれば良いのなら、初心者でもそれなりの高速で動くI/Oプログラムが作れます。中級レベルのスキルのプログラマーが作ったBSAMやEXCP(ブロック単位アクセス)のプログラムより、初心者が作ったQSAMのプログラム(レコード単位アクセス)の方が恐らく性能が高いでしょう。QSAMは手軽に利用できますが、実際は奥深いアクセス法でもあります。
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- OPEN (SYSIN,INPUT) OPEN SYSIN DATASET OPEN (SYSPRINT,OUTPUT) OPEN SYSPRINT DATASET : : READCARD DS 0H GET SYSIN,PARMCARD READ NEXT RECORD TO OUR AREA : : PUT SYSPRINT,PRTLINE WRITE PRINT OUT RECORD : : B READCARD LOOP UNTIL SYSIN E.O.D SPACE , ENDPROC DS 0H : : PUT SYSPRINT,SUMMARY WRITE PROCESSING SUMMARY CLOSE (SYSIN) CLOSE SYSIN DATASET CLOSE (SYSPRINT) CLOSE SYSPRINT DATASET : L R13,4(,R13) RESTORE CALLER SAVEAREA LM R14,R12,12(R13) RESTORE CALLER REGISTERS SLR R15,R15 SET CC = 0 BR R14 RETURN TO CALLER SPACE , PARMCARD DC CL80' ' PARAMETER CARD READ AREA PRTLINE DC CL132' ' PRINT LINE DATA SUMMARY DC CL132' ' SUMMARY DATA : SYSIN DCB DDNAME=SYSIN, + DSORG=PS,MACRF=GM,EODAD=ENDPROC, + RECFM=FB,LRECL=80 SYSPRINT DCB DDNAME=SYSPRINT, + DSORG=PS,MACRF=PM, + RECFM=FB,LRECL=132
ざっとまとめるとこんな感じのサンプルになります。処理したいデータをSYSINから読んで、実行結果をSYSPRINT(SYSOUT)に出すものです。
COBOLと比べてもOPENはOPEN、CLOSEはCLOSE、READがGET、WRITEがPUTとソースを書く手間は高級言語とほとんど変わりません。I/Oそのもののコードを書くこと自体はそれほどむずかしくないでしょう。
-
ここで解説されていないパラメーターなど、QSAMに関しての詳細はマニュアルを参照して下さい。
MVS:
「DFSMSデータ・セットの使用法」
「DFSMS Macro Instructions for Data Sets」
MSP:
「データ管理解説書」
「データ管理マクロ命令文法書」
VOS3:
「データ管理解説」
「データ管理マクロ」