【入門編】アセンブラレベルでのデータ例外(S0C7)の発生原因 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネス寄りの言語をバリバリ書いてきた方にとって、IBMメインフレームの世界は、時として「黒魔術」の領域に見えるかもしれません。

特に、深夜のバッチ処理で突如として画面(というかジョブのログ)に現れる、あの恐怖の文字……
「SYSTEM COMPLETION CODE=0C7」(通称:S0C7アベンド)

「一体何が起きたんだ!?」と冷や汗をかいた経験はありませんか?
大丈夫です。怖がる必要は全くありません。今回は、JavaやCOBOLではめったにお目にかからない、メインフレーム特有の「データの裏側」と、PL/Iにおけるスマートな防御策(バリデーション)について、優しく紐解いていきましょう。

—

そもそも「S0C7」って何をしている時に起きるの?

JavaやC言語で育った方なら、「数値の計算なんて、プロセッサが勝手にやってくれるでしょ?」と思われるかもしれません。もちろんその通りなのですが、メインフレーム(z/Architecture)の心臓部には、ビジネス計算を高速に行うための「十進演算(Decimal Arithmetic)」という専用のハードウェア命令が存在します。

この十進演算では、データを「パック10進数(Packed Decimal)」という特殊な形式でメモリ上に保持します。

パック10進数ってどんな形?(イメージしやすい例え)

例えば、Javaの `int` 型であれば、メモリ上に4バイトのビットパターンとして直感的に保存されますよね。
しかし、PL/Iの `PIC ‘999’` のような定義(数値データ)は、メインフレーム上では1バイトの中に「2桁の数字」と「最後の1バイトの右側に符号(プラス/マイナス)」をギュウギュウに詰め込んで表現します。

  • 良い例:「123」という数字

→ メモリ上には `01 23 3C` のように格納されます(末尾の `C` はプラスの意味)。CPUは「よし、全部きれいな数字だな!」と安心して計算します。

  • 恐怖の例:ここにバグやファイル転送のミスで、文字の「A」やスペース(空白)が混入したとします。

→ メモリ上には `01 23 C1` のような、「数字じゃない変なビットパターン」が入ってしまいます。

この状態でCPUが「さあ、この数字を使って足し算をするぞ!」と十進演算命令を発行した瞬間、CPUはこう叫びます。
「おいおい、ここに書いてあるのは数字じゃないぞ!データの意味がわかんねぇ!」

これが、S0C7(データ例外:Data Exception)の正体です。つまり、ハードウェアレベルの「数字じゃないものを数字として計算しようとしたエラー」なんです。

—

なぜPL/Iでこれが起きるのか?(PL/Iの懐の深さと罠)

他の言語、例えばCOBOLであれば、MOVE文の暗黙の転記やコンパイル時の厳格なチェックで弾いてくれることがあります。
しかし、PL/Iは「プログラマが意図したことは、可能な限り何でも実行する」という非常に懐の深い(言い換えればお節介な)言語です。

PL/Iには、Javaのような厳格な `int` や `String` といった「予約語としての型」がありません。その代わりに、ピクチャー句(`PICTURE`)やデータ属性を使って、変数を自由自在に定義します。

例えば、以下のようなPL/Iのコードを考えてみましょう。

DCL IN_RECORD CHAR(80); / 入力ファイルからの生データ(文字) /
DCL WS_AMT PIC ‘9(5)V99’; / 計算用のパック10進数項目(合計7桁、小数2桁) /

/ 外部から読み込んだ文字データを、そのまま計算用項目に代入! /
WS_AMT = SUBSTR(IN_RECORD, 1, 7);

Javaの感覚だと、「StringからBigDecimalへのキャスト」のようなイメージでしょうか。
もし、入力データ(`IN_RECORD` の先頭7桁)が綺麗に `1234567` であれば何も問題ありません。しかし、もしここに `A234567` や 全角スペース、あるいは未入力のパディング(Low-ValuesやSpaces) が混ざっていたらどうなるでしょう?

PL/Iはエラーを言わずに代入を試みます(あるいは内部で変換しようとします)。そして、その `WS_AMT` を使って次の行で四則演算を行った瞬間……ドカン!S0C7の完成です。

—

怖くない!PL/Iでのスマートなバリデーション手法

「じゃあ、外部から来るデータが汚れているかもしれないなら、どうやって身を守ればいいの?」
安心してください。PL/Iには、計算する前にデータが「本当に数字か」を安全にチェックする素晴らしい仕組みがあります。

それが、組み込み関数 `VERIFY` です。

実用コード例:S0C7を未然に防ぐ門番

以下のコードは、外部から読み込んだ文字データが、計算用のパック10進数(`PIC ‘9’`系)に代入しても安全かどうかを事前にチェックするサンプルです。

DCL IN_DATA CHAR(7) INIT(‘123 567’); / 例:スペースが混入している汚れたデータ /
DCL SAFE_AMT PIC ‘9(5)V99’; / 計算用の定義 /
DCL WORK_STR CHAR(7);
DCL ERR_FLG BIT(1) INIT(‘0’B); / エラーフラグ /

/ 1. まずは文字として安全に受け取る /
WORK_STR = IN_DATA;

/

  • 2. VERIFY関数で「0から9以外の文字」が含まれていないかチェックする
  • VERIFY(文字列, 探す文字のセット) は、セットに含まれない文字が最初に現れた位置を返します。
  • すべて許容文字(’0’〜’9’)であれば、0を返します。

/
IF VERIFY(WORK_STR, ‘0123456789’) ^= 0 THEN DO;
/ 数字以外(スペースや英字など)が混入している場合の処理 /
DISPLAY(‘【警告】数値項目に不正なデータが検出されました: ‘ || WORK_STR);
ERR_FLG = ‘1’B;
END;
ELSE DO;
/ 3. 安全を確認できてから初めて数値項目に代入する /
SAFE_AMT = WORK_STR;
DISPLAY(‘正常データです。計算を継続します。’);
END;

IF ERR_FLG THEN DO;
/ エラー時のバイパス処理や異常終了の手続きへ /
/ ここで処理を分ければ、S0C7アベンドでジョブが異常終了するのを防げます! /
END;

コードのポイント解説

  • `VERIFY(WORK_STR, ‘0123456789’)`

これが肝です。`WORK_STR` の中を1文字ずつスキャンし、`’0’` から `’9’` 以外の文字(スペースや記号、英字など)を見つけたら、その位置(1〜7)を返します。全部数字なら `0` を返します。非常にシンプルですが、メインフレーム開発ではお馴染みの強力なバリデーション関数です。

  • 必ず「文字(CHAR)」で受けてからチェックする

最初から `PIC ‘9’` の変数に不正なデータを入れようとすると、その瞬間にコンパイラやハードウェアが悲鳴を上げることがあります。まずは安全な `CHAR` 型で受け取り、`VERIFY` で検問を通してから、数値型(`PIC ‘9’`)に代入する。これがレガシーシステムの堅牢性を保つ黄金律です。

—

まとめ

JavaやCOBOLの経験がある方にとって、PL/Iの自由度の高さと、ハードウェアに直結したデータ構造(S0C7)は、最初は少しドキッとするかもしれません。

しかし、仕組みさえわかってしまえば怖くありません。

  • S0C7は、パック10進数(計算用領域)に数字以外のゴミが混ざったときのCPUの悲鳴。
  • 対策は、計算する前に `VERIFY` 関数などの文字チェック用バリデーションを挟むこと。

この2つを心に留めておくだけで、あなたの書くPL/Iプログラムは、深夜のバッチ運用者をも唸らせる「鉄壁の堅牢性」を手に入れます。

レガシーな世界へ踏み出したあなたのチャレンジを、心から応援しています!次回も実務で役立つPL/Iの知見をお届けしますので、どうぞお楽しみに。

タイトルとURLをコピーしました