CASL入門

Similar documents
主記憶の使われ方 システム領域 SP スタックポインタ システム用 スタック用 プログラム起動時に OS によって確 保される (SP が決められる ) プログラム用 メインルーチン プログラム領域 命令コードの列定数 変数用領域サブルーチン命令コードの列 先頭番地は リンク時に OS によって決め

CASL入門

CASL入門

COMET II のプログラミング ここでは機械語レベルプログラミングを学びます 1

コンピュータ工学Ⅰ

PowerPoint プレゼンテーション

Microsoft Word - VBA基礎(6).docx

Microsoft PowerPoint - ProcML-12-3.ppt

main.dvi

また RLF 命令は 図 2 示す様に RRF 命令とは逆に 各ビットを一つずつ 左方向に回転 ( ローテイト ) する命令である 8 ビット変数のアドレスを A とし C フラグに 0 を代入してから RLF A,1 を実行すると 変数の内容が 左に 1 ビットシフトし 最下位ビット (LSB)

ex05_2012.pptx

計算機アーキテクチャ

ソフトウェア基礎技術研修

書式に示すように表示したい文字列をダブルクォーテーション (") の間に書けば良い ダブルクォーテーションで囲まれた文字列は 文字列リテラル と呼ばれる プログラム中では以下のように用いる プログラム例 1 printf(" 情報処理基礎 "); printf("c 言語の練習 "); printf

命令セットの構成例 a) 算術 演算命令 例 )ADD dest, source : dest dest + source SUB dest, source : dest dest - source AND dest, source : dest dest AND source SHR reg, c

マウス操作だけで本格プログラミングを - 世界のナベアツをコンピュータで - プログラムというと普通は英語みたいな言葉で作ることになりますが 今回はマウスの操作だけで作ってみます Baltie, SGP System 操作説明ビデオなどは 高校 情

PowerPoint プレゼンテーション

JavaプログラミングⅠ

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

プログラミング実習I

PowerPoint プレゼンテーション

Microsoft Word - VBA基礎(3).docx

Microsoft PowerPoint - kougi7.ppt

フローチャートの書き方 プログラムの開始と終わり 処理の流れの表記 ( アルゴリズム ) 逐次型 ( 直線型 ) 分岐型 ( 開始 ) 処理 1 条件 条件 処理 2 の処理 の処理 の処理 ( 終了 ) 処理 3 プログラムや人間の判断などのアルゴリズムは基本的に 逐次型 分岐型 ループ型の組み合

コンピュータ工学講義プリント (7 月 17 日 ) 今回の講義では フローチャートについて学ぶ フローチャートとはフローチャートは コンピュータプログラムの処理の流れを視覚的に表し 処理の全体像を把握しやすくするために書く図である 日本語では流れ図という 図 1 は ユーザーに 0 以上の整数 n

Microsoft PowerPoint ppt

Microsoft PowerPoint - OS07.pptx

char int float double の変数型はそれぞれ 文字あるいは小さな整数 整数 実数 より精度の高い ( 数値のより大きい より小さい ) 実数 を扱う時に用いる 備考 : 基本型の説明に示した 浮動小数点 とは数値を指数表現で表す方法である 例えば は指数表現で 3 書く

(1) プログラムの開始場所はいつでも main( ) メソッドから始まる 順番に実行され add( a,b) が実行される これは メソッドを呼び出す ともいう (2)add( ) メソッドに実行が移る この際 add( ) メソッド呼び出し時の a と b の値がそれぞれ add( ) メソッド

Microsoft PowerPoint - 7.Arithmetic.ppt

模擬試験問題(第1章~第3章)

プログラミング実習I

メソッドのまとめ

Microsoft PowerPoint - 09.pptx

1.1 ラベル ラベルはカラム 1 から始まらなければならない ラベルの後にはコロン スペース タブ 改行が続いてよい ラベルはアルファベットかアンダーバーで始まり 英数字 アンダーバー クエスチョンマークを含んでよい ラベルは 32 文字までである デフォルトではこれらは大文字と小文字を区別するが

Microsoft Word - CygwinでPython.docx

ディジタル回路 第1回 ガイダンス、CMOSの基本回路

PowerPoint プレゼンテーション

sinfI2005_VBA.doc

プログラミング入門1

<4D F736F F D20438CBE8CEA8D758DC F0939A82C282AB2E646F63>

数はファイル内のどの関数からでも参照できるので便利ではありますが 変数の衝突が起こったり ファイル内のどこで値が書き換えられたかわかりづらくなったりなどの欠点があります 複数の関数で変数を共有する時は出来るだけ引数を使うようにし グローバル変数は プログラムの全体の状態を表すものなど最低限のものに留

/*Source.cpp*/ #include<stdio.h> //printf はここでインクルードして初めて使えるようになる // ここで関数 average を定義 3 つの整数の平均値を返す double 型の関数です double average(int a,int b,int c){

PowerPoint プレゼンテーション

文字コード略歴 よこやままさふみ社内勉強会 2012/05/18 文字コード略歴 Powered by Rabbit 2.0.6

今回のプログラミングの課題 ( 前回の課題で取り上げた )data.txt の要素をソートして sorted.txt というファイルに書出す ソート (sort) とは : 数の場合 小さいものから大きなもの ( 昇順 ) もしくは 大きなものから小さなもの ( 降順 ) になるよう 並び替えること

Microsoft PowerPoint - 5Chap15.ppt

PowerPoint プレゼンテーション

プログラミング基礎

3-4 switch 文 switch 文は 単一の式の値によって実行する内容を決める ( 変える ) 時に用いる 例えば if 文を使って次のようなプログラムを作ったとする /* 3 で割った余りを求める */ #include <stdio.h> main() { int a, b; } pri

ネットワーク工学演習 解答編 典型的な IP アドレス問題と解答を示す 解き方をよく覚えるように N 科 ある PC がある ネットワークの設定をみると IP アドレスが であり サブネットマスクは である 下記について解答せよ [1]

<4D F736F F D2094F795AA95FB92F68EAE82CC89F082AB95FB E646F63>

Outlook2010 の メール 連絡先 に関連する内容を解説します 注意 :Outlook2007 と Outlook2010 では 基本操作 基本画面が違うため この資料では Outlook2010 のみで参考にしてください Outlook2010 の画面構成について... 2 メールについて

- VHDL 演習 ( 組み合せ論理回路 ) 回路 半加算器 (half adder,fig.-) 全加算器を構成する要素である半加算器を作成する i) リスト - のコードを理解してから, コンパイル, ダウンロードする ii) 実験基板上のスイッチ W, が, の入力,LED, が, の出力とな

初めてのプログラミング

PowerPoint プレゼンテーション

TOPPERS活用アイデア・アプリケーション開発

Transcription:

2 章 システム COMETⅡ の仕様 ここでは 情報処理推進機構 (IPA) が発行している情報処理技術者試験のパンフレットにある アセンブラ言語の仕様 にそって説明していきます この資料では システム COMETⅡの仕様 の中に命令の説明が書かれており アセンブラ言語 CASLⅡの仕様 には命令の説明は記載されていません 何か奇異な感じがしますが アセンブラの命令はハードウェア命令そのものであるので このような構成になっているのです - 1 -

2.1 ハードウェアの仕様 2.1.1 語 情報処理推進機構 (IPA) の説明書によれば ハードウェアの仕様として (1) から (6) まで説明が書 かれています 以下 順に説明していきます (1) 1 語は 16 ビットで, そのビットビット構成構成は, 次のとおりであるのとおりである コンピュータのメモリーの最小単位は ビット で 1 ビット 2 ビット というふうに数えます 1 ビットは 1 か 0 かの 2 種類の情報を表現できますが このままでは使いにくいので いくつかの ビットをまとめて使うことにしています CASLⅡ の場合は 16 ビットをひとつの かたまり としてあつかい この あつまり を 語 1 と呼んでい ます 2.1.2 主記憶 2.1.3 数値 2.1.4 制御方式 この 語 は単にメモリーの区切り ( グループ化 ) だけに使用するのではなく 後述する 汎用レジス タ の大きさも同様にしてあります (2) 主記憶の容量容量は 65536 語で, そのアドレスアドレスは 0 ~ 65535 番地であるである 上記 (1) で説明した 語 が集まってコンピュータのメモリー ( 主記憶 ) を形成しています COMETⅡ では この主記憶の大きさが 65536 語あって 各語には 0~65535 までの 65536 個の 名 前 がつけられています ビット語主記憶 16 集まって 65536 集まって 図 2-1 ビット 語 主記憶 この 名前 を 番地 ( アドレス ) と呼んでいます 65536 という数字は 中途半端な数字だな ~ と感じるかもしれませんが この数字はちょうど 2 の 16 乗になるのです では なぜ 16 乗 なのかといいますと (5) で説明するように 汎用レジスタの大きさが 16 ビットであ るところから来ています では メモリーが 65536 語以上ある場合はどうでしょうか 残念ながら COMETⅡ では 65536 以上は使 用することができないのです 理由は 2 章の 実効アドレス のところで説明します (3) 数値は,16 ビットの 2 進数で表現表現するする 負数負数は,2 の補数補数で表現表現するする この説明は 1 章で行いました (4) 制御方式は逐次制御逐次制御で, 命令語は 1 語長又は 2 語長であるである 逐次制御 と書くと難しそうですが 要するに 命令を順番に実行していく というもので ごくアタリ 1 * 語 は ご と発音しますが 呼びにくいとかとカッコ良くないなどの理由から ワード と呼ばれることもあります - 2 -

マエの話です しかし 自動制御や携帯電話などのプログラムは逐次制御ではありません 1 アセンブラは ( 機械語は ) 命令の形でメモリーに順序良く格納されています COMETⅡではこの命令の長さが 1 語または 2 語 2 ということですので 1 語 (=16 ビット ) で表現される命令と 2 語 (=32 ビット ) で表現される命令の 2 種類があるということです このあたりは 第 4 章で詳しく説明します 2.1.5 レジスタレジスタの種類 (5) レジスタとして,GR GR(16 ビット ),SP SP(16 ビット ),PR PR(16 ビット ),FR FR(3 ビット ) の 4 種類があるがある アセンブラでは 計算やデータのコピーなどは すべて レジスタ と呼ばれるモノ ( ハード ) を介して行われます この説明は次項で 汎用レジスタ GR( 汎用レジスタ,General Register) は,GR0 ~ GR7 の 8 個があり, 算術, 論理, 比較, シフトなどの演算演算に用いるいる アセンブラでは何の処理を行うにも汎用レジスタを経由します たとえば 変数 A の内容を変数 B へ転送する という処理の場合 C 言語 b=a; COBOL MOVE B TO A. と記述します しかし アセンブラでは直接変数間で転送を行うことができず 3 次の順序で処理をします 1 汎用レジスタへ変数 A の内容を持ってくる ( ロードする ) 2 汎用レジスタの内容を変数 B へ格納する ( ストアする ) 変数 A レジスタ変数 B 図 2-2 アセンブラでのでの変数変数のコピー この例では 転送 ( コピー ) するデータの長さが 1 語なので 上記のような対応で済みますが データの長さが 2 語とか 3 語の場合は この一連の処理を 2 回なり 3 回なり繰り返さなければなりません 更に 1000 語にもなれば 1000 回記述するわけにも行きませんから 何らかのループ処理が必要になります 1 話が難しくなるので 詳しい説明はしません 2 説明書に定義されているわけではありません 本書の勝手な設定です 詳しくは 3 章で 3 IBM 汎用機のアセンブラでは可能です - 3 -

また 変数 A の内容と変数 B の内容を加算し 結果を変数 C へ設定する ような処理では C 言語 c=a+b; COBOL COMPUTE C = A + B. または ADD A B GIVING C. と記述します このような場合でもアセンブラでは 1 汎用レジスタへ変数 A の内容をロードする 2 汎用レジスタへ変数 B の内容を加算する 3 汎用レジスタの内容を変数 C へストアするという処理になります 大変面倒です 変数 A 1 変数 B 2 変数 C 3 レジスタ 図 2-3 アセンブラでのでの加算処理 このように 処理に必須 な汎用レジスタですので 数もひとつだけではなく 8 個用意されています また 8 個の汎用レジスタには 0~7 までの番号がついており 汎用 ということで GR0~GR7 とも記述します (GR:General Register) 指標レジスタこのうち,GR1 ~ GR7 のレジスタレジスタは, 指標レジスタ (Index Register egister) としてアドレスアドレスの修飾修飾にもにも用いる 指標レジスタは 指標レジスタ という特定のレジスタがあるわけではなく 汎用レジスタで代用します ただし GR0 は使用できません ( 理由は第 4 章で ) ので GR1~GR7 までの 7 個が指標レジスタとしても使用できることになります では 指標レジスタはどのような場合に どのようにして使用するのでしょうか 上記 汎用レジスタ のところで述べましたが 今 変数 A から始まる 1000 語の領域を 変数 B から始まる 1000 語の領域へ転送する 場合 1 汎用レジスタへ変数 A の内容を持ってくる ( ロードする ) 2 汎用レジスタの内容を変数 B へ格納する ( ストアする ) この処理を 1000 回コーディングするのは現実的ではありません このような場合 高級言語では次のようなコーディングになるでしょうか - 4 -

C 言語 b[i]=a[i]; これを 1000 回繰り返す ( 実際は strcpy などの関数を使用するのでしょうけれど ) COBOL MOVE A(I) TO B(I). これを 1000 回繰り返す ( 実際は集団項目を確保し 1 回の MOVE 命令で済ますのでしょうけれど ) アセンブラでも同様にインデックスを順に変化させて 1 語ずつ転送します このときのインデックス ( 上記例では i ) の役目をするのが指標レジスタになります 詳細は第 2 部で 単に 変数 A としないで わざわざ 変数 A から始まる としたのは 次の理由からです 高級言語では 変数を確保した場合 領域の大きさにかかわらず領域全体が変数名と対応します たとえば 次のようなコーディングでは 1000 バイトの領域が確保されます C 言語 char a[1000]; COBOL A PIC X(1000). アセンブラでは A DS 1000 とした場合 変数 A に 1000 語の領域を割り当てる のではなく 1000 語の領域を確保し その先頭アドレスに A という名前をつける という意味になります スタック SP( スタックポインタ,Stack Pointer) は, スタックの最上段最上段のアドレスアドレスを保持保持しているしている まず スタック について説明します スタックとは 記憶領域の一種ですが ここへは PUSH 命令でデータを 溜め込む ことができ POP 命令で取り出すことができます ただし POP 命令では PUSH 命令で 最後に溜め込んだデータ が取り出されます つまり FILO (FirstIn LastOut) 方式になっています 今 スタックへ 100 個のデータを溜め込んだとします このとき POP 命令で最初に取り出されるのは 最後に PUSH したデータになるわけです 箱へレンガを入れることを想像してください レンガは箱の口と同じ大きさで 何段も積み重ねることができます スタックは こんなイメージです ここへ レンガを 1 個入れると箱の口からレンガが見えます レンガを次々と入れると 前に入れたレンガは後で入れたレンガで隠されるので 箱の口から見えているのは 常に最後に入れた連歌になります ここで レンガを上から取り出すと 最後に入れたレンガが取り出され 更に取り出すと一つ前に入れたレンガが取り出されます 最初に入れたレンガは最後にならないと取り出すことができません スタックへデータを入れるには 通常 PUSH 命令を使用し 取り出しは POP 命令で行います - 5 -

PUSH POP 図 2-4 PUSH と POP スタックへデータを入れるのは このほかに CALL 命令があります また RET 命令で取り出しが行われます この詳細については 命令の説明のところで述べます イメージとしてはこのとおりなのですが スタック機能を実装する場合 次の2つの方法が考えられます 1スタック領域の先頭から 後方 ( アドレスの増加方向 ) へ向かって順にスタックしていく方式 2スタック領域の最後から 前方 ( アドレスの減少方向 ) へ向かって順にスタックしていく方式いずれの方式が採用されているかは定義されていません 1 が どちらの方式を採るにしても 現在どこまでデータをスタックしているか を覚えておく必要があります ( ユーザー責任で覚えるのではなく CASLⅡのシステムとして ) この どこまでデータが格納されているか を覚えておくのが スタックポインタ です プログラムレジスタ PR( プログラムレジスタ,Program Register) は, 次に実行実行すべきすべき命令語命令語の先頭先頭アドレスアドレスを保持保持している 説明では 次に実行すべき命令の先頭アドレスを保持している となっています 詳しい説明の前にコンピュータが命令を規則に従って順に処理していく仕組みはどのようなものでしょうか 通常 プログラム ( 命令群 +データ群 ) はメモリー上に展開されています ( このような方式を ストアード プログラム Stored Program 方式 といいます ) プログラムの開始に先立ち オペレーティングシステムは プログラムの最初の命令が格納されているアドレスをプログラムレジスタへ格納します その後の命令の実行は次のようになります 1 最初の命令を プログラムレジスタの示すアドレスから取り出す (1 語 ) 2 命令の長さが 2 語 (4 章で詳しく説明します ) の場合は 次のアドレスから命令の続きも取り出す 3 命令の長さが 1 語のときは 1 を 2 語のときは 2 をプログラムレジスタへ加算する 4 命令を実行する 51へ戻るこのようにして 順にプログラムを実行していきます ( 上記 1~3をフェッチサイクル Fetch cycle 45をエグゼキュートサイクル Excecute cycle と 1 PUSH 命令 CALL 命令ではスタックポインタの値が 1 だけ減少し 逆に POP 命令 RET 命令では 1 だけ増加すると 定義されていますので 2 の方式を採用しているようです - 6 -

呼びます ) それでは分岐命令はどうするのでしょうか 高級言語で言う C 言語 goto ラベル ; COBOL GO TO ラベル. という命令です 分岐命令では 分岐先のアドレスをプログラムレジスタへ格納します 上記 4の 命令を実行する という動作は 分岐命令では まさに 分岐先のアドレスをプログラムレジスタへ格納 することになるのです プログラムレジスタは CALL 命令や RET 命令でも ちょっと変わった動きになりますが これは 4 章で説明します フラグレジスタ FR( フラグレジスタ,Flag Register) は,OF OF(Overflow Overflow Flag), ),SF SF(Sign Sign Flag), ),ZF ZF(Zero Zero Flag) と呼ぶ 3 個のビットビットからなり, 演算命令などのなどの実行実行によってによって次の値が設定設定されるされる これらのこれらの値は, 条件付き分岐命令分岐命令で参照参照されるされる プログラムを作成するときには いろいろな判断が必須になります 大小関係 ある値に等しいか 正負判断などいろいろですが 判断の後で命令を記述し 常に判断とペアでプログラムを作成します たとえば 変数 A が 1 なら 変数 B に 1 を加算する という場合 C 言語 if (a==1) b++; COBOL IF A = 1 ADD 1 TO B. というようになり 決して 判断のみ の命令というものはありません つまり C 言語 if (a==1) COBOL IF A = 1 だけではプログラムとしては意味の無いものになります ( 場合によってはコンパイル時にエラーになります ) ところがアセンブラでは 判断 とそれに続く 命令 は別々に記述します 上の命令をアセンブラで記述するには 次のような順序になります 1 変数 A の内容を汎用レジスタへロードする 2 汎用レジスタと 1 を比較する (1 が格納されているアドレスの内容 ( つまり 1 と比較する) 3 比較の結果が保存される 4 保存された結果を見て 分岐するかどうかを決定するとなります このとき 3で比較結果を保存しますが この 保存する 場所がフラグレジスタになります 4では フラグレジスタの内容を見て分岐を決めることになります - 7 -

( アセンブラでは 判断のあとは 分岐 命令しか記述できません 1 を加算する というような命令は 分岐先で記述します 詳細は 5 章で ) フラグレジスタに値が設定されるのは 比較命令だけではなく 次のような命令でも設定されます 比較命令 ( 算術比較 論理比較 ) ロード命令算術加算 論理加算 算術減算 論理減算論理積 論理和 排他的論理和シフト命令 ( 算術シフト 論理シフト ) このように 多くの ( というよりは ほとんどの ) 命令でフラグレジスタの内容が変わります ところで 判断は必ずしも比較命令のみで行うわけではありません 上述のように ロード命令 でもフラグレジスタが設定されるので ある変数が正 負 ゼロかどうかというような判断をする場合 比較をしなくても判断することができます ある変数をレジスタへロードすると その内容 ( 正 / 負 / ゼロ ) によってフラグレジスタには異なる値が設定されますので あとは分岐命令を記述すればよいのです 以下 3 種類のフラグレジスタについて説明します OF 算術演算命令の場合場合は, 演算結果が -32768 ~ 32767 に収まらなくなったとき 1 になり, それ以外のとき 0 になる 論理演算命令論理演算命令の場合場合は, 演算結果が 0 ~ 65535 に収まらなくなったとき 1 になり, それ以外以外のとき 0 になる 算術演算は (CASLⅡでは) 足し算と引き算のみが定義されています 足し算 または引き算の結果が -32768~+32767 の間に収まらないときに OF が 1 に設定され 収まるときには 0 に設定されます 例えば 20000+20000 は 32767 より大きいので 1 が設定されます 20000+12767 はちょうど 32767 となり 0 が設定されます ちなみに -32768~+32767 というのは レジスタが 16 ビットで うち 1 ビットは符号で使用しているため このような制限になります また 論理演算では符号がありませんから 最上位ビットもすべて数値の一部と考えて 0~65535 までとなります 0 0000 0000 0000 0000 65535 1111 1111 1111 1111 表 2-1 符号なしなし数値数値の下限下限と上限 SF 演算結果の符号符号が負 ( ビット番号 15 が 1) のとき 1, それ以外以外のとき 0 になる ZF 演算結果が零 ( 全部のビットビットが 0) のとき 1, それ以外以外のとき 0 になる 演算結果 符合については 1 章を参照してください - 8 -

論理演算 (6) 論理加算又は論理減算論理減算は, 被演算データデータを符号符号のないのない数値数値とみなして, 加算又は減算減算するする これについては 算術 論理演算命令 のところで 詳しく説明します 2.2 実効アドレス 命令を学習するに先立ち 実効アドレスについて説明します 実効アドレス説明にもあるとおり 実効アドレスとは adr で示されるアドレスと指標レジスタの内容を加算し 65536 で割った余り をいいます 指標レジスタは 説明した通りインデックスの代わりとして用いますから adr に加算するとして なぜ 65536 で割るのでしょうか 理由はふたつあります 理由 1: このハードのメモリーが 65535 番地までしか無いからです これ以上のアドレスは存在しませんから使いようがありません 理由 2: これはちょっと難しいのですが このハードは 16 ビットを基本として設計されています 16 ビットで表現できる最大の値 ( 符号を考慮しないとして ) が 65535 で これを越えるとオーバーフローして 残り ( つまり剰余 ) が 0~65535 までになるからです 例えば 3 桁しかないソロバンを考えてみてください いま 900 が置かれているとして これに 200 を加えるとどうなるでしょうか 答えは 1100 ですが 3 桁しかありませんから 千 の位はなくなって 残っているのは 100 だけになります これと同じように CASLⅡでも 16 ビットで表現できる範囲 を超えた場合 超えた部分がなくなってしまいます 2.3 命令 説明書によれば 命令は大きく次のように分類 1 されています 1 ロード ストア ロードアドレス命令 2 算術 論理演算命令 3 比較演算命令 4 シフト演算命令 5 分岐命令 6 スタック操作命令 7 コール リターン命令 1 IBM のメインフレームでは スタック がありませんので スタック操作命令 というのはありません また コール リターン命令 は 分岐命令 に分類されます また その他 の ノーオペレーション命令 も 分岐命令 に分類されます - 9 -

8 その他以下 順に説明します 2.3.1 ロード命令ロード命令にはふたつの書き方があります それぞれについて説明します LD r1,r2 この命令は r2 の内容を r1 へコピーするものです 仮に 汎用レジスタ 3 に 1000 という値が入っていたとします LD GR1,GR3 これで 汎用レジスタ 1 にも 1000 という値が設定されます 汎用レジスタ 1 の元の値が何であれ 1000 に設定されます 一方 GR2 の値はそのままかわらずに残ります レジスタの書き方として たとえば汎用レジスタ 1 を指定する場合 説明書では GR1 と書くと定義されています IBM メインフレームのアセンブラ言語では単に 1 と書き 他の書き方はエラーになります しかし CASLⅡでは LD 1,2 と書いた場合 2 は 汎用レジスタ 2 を指すのか それとも 2 番地 を指すのか判別できないことになってしまいます そこでこれを区別するためには 汎用レジスタは GR を付けて書くことになります ただし この場合 変数名として GR0 から GR7 は使用できないことになります アセンブラ言語で予約語があるというのも首をかしげるところではありますがまた 以降は 汎用レジスタ という言葉の代わりに GR と書くことにします この命令は 一体何に使用するのでしょうか 単に レジスタの値をコピーする目的もありますが レジスタの内容が正の値 / 負の値 / ゼロかを判 定する場合にも使用します この命令実行後に r1 の値 ( つまり r2 の値 ) によりフラグレジスタが次のように設定されます OF SF ZF r1>0 0 0 0 r1=0 0 0 1 r1<0 0 1 0 表 2-2 LD 命令後のフラグレジスタフラグレジスタの値 OF は常にゼロが設定されますので SF と ZF の値から r1 が正 / 負 / ゼロのいずれであったかを判 定できます - 10-

ところで LD GR1,GR1 という命令を実行するとどのような結果になるのでしょうか これは GR1 を GR1 へコピーする命令ですから 何も変化は無いように見えます 確かに 汎用レジスタやメモリーの内容は変化しませんが フラグレジスタがセットされます LD r,adr[,x] この命令では実効アドレス (adr[,x]) の内容が汎用レジスタ r へコピーされます LD GR1,1000 この命令では 1000 番地の内容が GR1 へコピーされます 1000 番地の内容は変化しません 通常プログラムを作るときは 1000 などの直接数値を書くことはあまりありません 通常は変数を書くことになります たとえば LD GR1,KINGAKU というように書き アセンブラ ( コンパイラ ) が KINGAKU を 1000 に変換します 詳細は プログラムの演習のところで説明します GR1 へは 1000 番地の内容内容 がコピーコピーされるのであってされるのであって 1000 がセットセットされるのではありませんされるのではありません それでは インデックス (x) が指定されている場合はどうなるでしょうか 今 GR2 に 500 が入っているとします LD GR1,1000,GR2 この命令では 1000 と GR2 の中身である 500 が加算されて (1000+500=1500) 1500 番地の内容が GR1 へコピーされます 1500 番地の内容は変化しません なお どちらの場合でもコピーされた値によってフラグレジスタが更新されます 2.3.2 ストア命令ストア命令は ロード命令と反対の動きをします つまり 汎用レジスタの内容が実効アドレスの番地へコピーされます 汎用レジスタの内容は変化しません ST r,adr[,x] いま GR2 に 500 が入っているとします ST GR1,1000,GR2 この命令では GR1 の内容が 1500 番地へコピーされます 汎用レジスタの内容は どれも変わりません インデックスを使用しない場合は ST GR1,1000-11-

のように記述します ストア命令では フラグレジスタも設定されませんので そのまま残っています ところで ストア命令には ST r1,r2 という形式の書き方はありません 仮に あった としたら ST GR1,GR2 とでもなって GR1 の内容を GR2 へコピーする という動作になるのでしょうか でも これは LD GR2,GR1 とまったく同じ動作 1 になります 同じ動作であれば 命令を二つ用意することもないわけです 2.3.3 ロードアドレス命令この命令はロード命令と似ていますが 名前の通り アドレスそのもの をレジスタへ設定します LAD GR1,1000 この命令では 実効アドレスそのものが GR1 に設定されます つまり 1000 が設定されます LD GR1,1000 では 1000 番地の内容内容 が設定されましたが LAD 命令では 1000 そのものが設定されるわけです 仮に 1000 番地の内容が 300 だった場合 LD 命令では 300 が設定されます 1000 番地 1000 300 300 LAD 命令 LD 命令 図 2-5 LAD 命令と LD 命令それでは インデックスを使用した場合はどうでしょうか 仮に ( 例によって ) GR2 に 500 が入っているものとします LAD GR1,1000,GR2 この命令では 実効アドレス 1500(1000+GR2 の中身の 500) が GR1 に設定されます つまり GR1 は 1500 になります くどいようですが 1500 番地の中身 が設定されるのではありません 1 厳密には フラグレジスタが設定されない点が ロード命令とは異なりますが - 12-

それでは 次の命令はどうなるでしょうか LAD GR1,1,GR1 GR1 の内容を x とします 実効アドレス部分は 1,GR1 ですから実効アドレスは 1+x になり これが GR1 に設定されます 元の GR1 の中身x 命令実行後の中身x+1 つまり GR1 に +1 する命令というわけです LAD GR1,5,GR1 と書けば GR1 に 5 が加算されますし LAD GR1,-2,GR1 と書けば GR1 から 2 が引かれることになります なお ロードアドレス命令ではフラグレジスタは変化しません ところで この命令は汎用レジスタにある数を加えたり引いたりするほかに 何に使うのでしょうか これは プログラム演習 の章で説明します アセンブラ言語の醍醐味が このロードアドレス命令なのです 2.3.4 算術加算命令何やらいかめしい名前ですが 要するに 足し算 の命令です 算術加算 というからには 算術でない加算 という変な命令もあるのでしょうか あります 論理加算 という命令です ( 後述します ) CASLⅡでは 算術 と 論理 を レジスタの一番左のビットを符号としてあつかうか否か で分けています 符号として扱う命令が 算術 という命令で 扱わないのが 論理 という命令になります ADDA r1,r2 もうお分かりかと思いますが この命令では 汎用レジスタ r2 の内容を r1 へ加算し 結果が r1 に入ります r2 の内容は変化しません 今 GR1 に 100 GR2 に 200 が入っていたとすれば ADDA GR1,GR2 で 汎用レジスタ 1 には 300 が格納されます 汎用レジスタ 2 は元のままです ADDA r,adr[,x] この記述では 実行アドレスの内容が汎用レジスタ r に加算されます 実効アドレスの内容は変わりません - 13-

いま 汎用レジスタ 1 に 100 汎用レジスタ 2 に 200 ( メモリーの )1200 番地には 300 が入っていたとすれば ADDA GR1,1000,GR2 adr が 1000 インデックス( 汎用レジスタ 2) には 200 が入っていますから 実効アドレスは 1200 になりますので 1200 番地の内容である 300 が汎用レジスタ 1 の 100 に加算されて 結果 汎用レジスタ 1 には 400(100+300) が設定されます 汎用レジスタ 2 に内容も 1200 番地の内容も変化はありません 算術加算命令では どちらの書き方でもフラグレジスタが設定されます OF SF ZF r1>0 *1 0 0 r1=0 *1 0 1 r1<0 *1 1 0 *1 計算結果が -32768~+32767 の間に収まらない場合は 1 に 収まれば 0 になる 1 2.3.5 論理加算命令 表 2-3 ADDA 命令後のフラグレジスタフラグレジスタの値 基本的には算術加算と同じですが 符号を考慮しない点が算術加算と違うところです 符号を考慮 しませんから 負の数値という概念は無く 0~65535 までの範囲の値になります 書き方 考え方は算術加算と同じです フラグレジスタのうち OF については設定のされ方が算術加算とは少し異なります 論理 加算です から -32768~+32767 までではなく 0~+65535 までの間に収まらないときに OF が設定されます 2.3.6 算術減算命令 足し算が引き算になるだけで 考え方は算術加算と同じです SUBA r1,r2 この書き方は r1 の内容から r2 の内容を引き算し 結果を r1 に設定します R2 の内容は変わりま せん SUBA r,adr[,x] これも算術加算と同じで r から実効アドレスで示されるアドレスの内容を引き算します 今 汎用レジスタ 1 に 1000 汎用レジスタ 2 に 300 1300 番地の内容が 600 であるとき SUBA GR1,1000,GR2 は どうなるでしょうか 実効アドレス 1000+300( インデックスの内容 ) 実効アドレスの内容 (1300 番地の内容 ) 600 したがって 1000-600=400 が GR1 に設定されます GR2 の内容 1300 番地の内容は ともに変化し 1 オーバーフローした場合 加算結果がどうなるかについては規定されていないが オーバーフローした残りの部 分 ( 加算結果を 32768 で割った余り ) が設定されると思われる - 14-

ません 2.3.7 論理減算命令符号を考慮しない減算命令で 考え方は算術減算と同じです 2.3.8 論理積 論理和論理和 排他的論理和命令それぞれ 汎用レジスタと 汎用レジスタまたは実行アドレスの指す番地の内容との間で ビット毎に論理積 論理和 排他的論理和をとり 結果を汎用レジスタへセットする命令です 演算は 1 語 16 ビットすべてのビットについて行われます AND r1,r2 r1 と r2 の論理積をとり 結果を r1 へセットする OR r1,adr1 r1 と ADR1 番地の内容とで論理和をとり 結果を r1 へセットする 論理積 論理和 排他的論理和の演算は次のように定義されています 論理積 論理和 排他的論理和 ビット 1 表 2-4 論理演算の結果 例 ビット 2 結果説明 0 0 0 0 1 0 1 0 0 1 1 1 0 0 0 0 1 1 1 0 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 0 それでは実際の例を見てみましょう 両方が 1 のときのみ結果が 1 になる 少なくともどちらか片方が 1 のとき 1 となる 両方が異なるとき 1 となり 同じ時は 0 となる 今 GR1 がビットで 0001 0100 1010 1111 で GR2 が 1011 0011 1000 0101 だったとします AND GR1 GR2 とすると GR1 0001 0100 1010 1111 GR2 1011 0011 1000 0101 AND 結果 0001 0000 1000 0101 となりますので この AND 結果が GR1 に設定されます GR2 の内容は変化しません フラグレジスタは3つとも 0 が設定されます ところで これらの命令はどんなときに使用するのでしょうか 加算命令や減算命令は使い道がわかるのですが これらの命令はちょっと ( 使い道の ) 想像がつきませんね ということで 例をあげてみます - 15-

AND 命令 よく使用するのは ある特定のビットが 0 か 1 かを判断する 場合です 例えば 汎用レジスタに 0001 0000 0000 0000 をセットしておき 1 これをメモリーとの間で論理積演 算します 結果が 全ビットゼロになれば ( つまり フラグレジスタ ZF がゼロであれば ) メモリーの左から 4 ビット 目は もとは 0 だった ということになりますし ZF がゼロでなければ メモリーの左から 4 ビット目は もとは 1 だった ということになります OR 命令 COMETⅡ では ( どのコンピュータもそうですが ) 文字に 16 進コードが割り振られており これを 文字 コード と呼んでいます COMETⅡ では 1 バイト文字 2 のみ定義されており このうち数字の 0 ~ 9 は それぞれ X 30 ~ X 39 に割り振られています いま 汎用レジスタに 2 進数の数値 ( ただし 0~9) があったとき これを文字に変換するにはどうす れば良いでしょうか COMETⅡ では 文字 は 1 語に 1 文字で格納され 上位 8 ビット (1 語の左半分 ) は 0 になると定義さ れていますから X 0000 X 0030 X 0001 X 0031 X 0009 X 0039 に変換するわけです このような場合 いちばん簡単なのは X 0030 で論理和をとる という方法です たとえば 5(X 0005 ) と X 0030 の論理和は X 0035 になりますので 文字に変換されたこ とになります 逆に文字から数値への変換は X 000F で論理積をとればよいことになります ( 確認してみてくださ い ) XOR 命令 これは難物です あまり 良い使用例が思い浮かびません あまり役立つとは思えませんが 次のような例はどうでしょうか 今 GR1 0001 0101 1100 1110 GR2 1101 0010 1010 0000 が格納されていたとします これに次の演算を行います 1 XOR GR1,GR2 2 XOR GR2,GR1 3 XOR GR1,GR2 1 LD GR1,=#1000 または LAD GR1,#1000 でセットできる 2 1 バイト =8 ビットなので 最大 256 種類しか定義できない 漢字コードなどはこれでは不足なので 16 ビットが割り 振られており これを 2 バイトコードという - 16-

順にやってみましょう 1 は GR1 0001 0101 1100 1110 GR2 1101 0010 1010 0000 結果 GR1 1100 0111 0110 1110 2 は GR2 1101 0010 1010 0000 GR1 1100 0111 0110 1110 結果 GR2 0001 0101 1100 1110 3 は GR1 1100 0111 0110 1110 GR2 0001 0101 1100 1110 結果 GR1 1101 0010 1010 0001 さて なにか気づきましたでしょうか そうです GR1 と GR2 の内容が入れ替わっていますね ( 単にそれだけですが ) 2.3.9 比較演算命令 比較演算命令は 値の大小を比較し 結果をフラグレジスタへ設定する命令です 通常この命令の 後はジャンプ命令となります 比較には 算術比較と論理比較があります 算術比較は比較する値を符号付き数値 ( 最左ビットを符号として扱う ) として比較しますから 比較す る数値の範囲は -32768~+32767 の間となります 論理比較では最左ビットを符号としては見ないで 数値の一部として判断します 従って比較できる 範囲は 0~65535 までの間となります 次の表は数値 1 と数値 2 を それぞれ算術比較と論理比較でどのような比較結果になるかをまとめ たものです ( 値はいずれも 16 進表示です ) 1 数値 1 0001 8000 FFFF 8000 2 数値 2 0000 7FFF 0000 FFFF 算術比較 1>2 1<2 1<2 1>2 論理比較 1>2 1>2 1>2 1<2 表 2-5 論理比較と算術比較算術比較の結果 比較は 汎用レジスタ同士 または汎用レジスタと実効アドレスの内容とで行われます 前述しましたように比較命令は 比較して フラグレジスタに結果を設定する 命令です 比較の後 には ( その結果次第で ) ジャンプしたり 何らかの演算 更に比較などを行いますが CASLⅡ では ジャンプ命令しか記述できません ( 記述はできますが フラグレジスタの値によって何かを行うという 命令は ジャンプ命令しかありません ) 比較命令の後に フラグレジスタの内容を変化させない 命令 例えばストア命令やロードアドレス 命令などをはさんで その後にジャンプ命令を書いてもかまいませんが バグの元になりますので避 けるほうが賢明です 今 次のような処理をすることを考えてみます 1 GR1 の内容を変数 A へ格納する 2 GR1 と GR2 を比較し GR1>GR2 であれば NEXT へジャンプする - 17-

通常は 次のように記述します ST CPA JPL GR1,A GR1,GR2 NEXT それでは 次のような書き方はどうでしょうか CPA ST JPL GR1,GR2 GR1,A NEXT これでもプログラムは同じように動作しますが 少し不自然です 下の例では JPL 命令は どの命令の結果を見るのだろう という疑問が湧きます ST 命令ではフラグレジスタは変化しませんので CPA 命令の結果を見ていることは明白ですが もしかしたら ST 命令でもフラグレジスタが変化するのだろうか と一瞬でも考えてしまいます 2.3.10 シフト命令シフト命令は 汎用レジスタの 16 ビットを左または右へ指定されたビット数だけ移動させる命令です 汎用レジスタは 16 ビットしかありませんので 17 ビット以上のシフトは意味がありません シフト命令も加算 減算命令と同様に 算術シフト と 論理シフト があり 算術シフトは符号を考慮した命令で 論理シフトは考慮しない命令です 2.3.11 算術シフトシフト命令左シフトと右シフトがあります 左シフト ( 算術 ) 次の図のように ビットを移動します 次の例は 左へ 2 ビット分算術シフトさせた例です ビット位置 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 シフト前 0 1 1 0 0 1 1 0 1 0 0 0 1 1 1 0 シフト後 1 0 0 1 1 0 1 0 0 0 1 1 1 0 図 2-6 算術左シフト ( 誤 ) それでは シフトによって こぼれ落ちる ビット ( 上図の黄色の部分 ) はどうなるのでしょうか これは 無くなって しまいます また シフトによって発生した 隙間 のビット ( 図の水色の部分 ) には何が入るのでしょうか ここにはすべて 0 が格納されます それではシフトはどのような場合に使用されるのでしょうか 一番分かりやすいのが 乗算 と 除算 です 左へ 1 ビットシフトするということは 2 倍する ということになり 左へ n ビットシフトするということは 2 n 倍するということになります 逆に右へシフトするということは除算になります 右へ n ビットシフトするということは 2 n で割るということになります - 18-

このように乗算や除算に使用できますが 残念ながら 2 n 倍する場合や 2 n で割る場合にしか使用できません ここで質問です 負 の数値は最左ビット(15 ビット目 ) が 1 だったはず 上の例だと 負の数値の場合 左シフトを行うと状況によって正の数になってしまうことがあります 右シフトでは ( 隙間に 0 が詰まりますから ) 必ず正の数になってしまいます これはおかしいですね 実は 上図が間違っているのです 正しくは次のようになります ビット位置 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 シフト前 0 1 1 0 0 1 1 0 1 0 0 0 1 1 1 0 シフト後 0 0 0 1 1 0 1 0 0 0 1 1 1 0 図 2-7 算術左シフト ( 正 ) シフトは 0~14 ビット目の 15 ビットで行われ 最左 (15 ビット目 ) は常に 15 ビット目へコピーされます 従って こぼれ落ちる のは 13 ビット目と 14 ビット目になります 右シフト ( 算術 ) では 右へ算術シフトする場合はどうなるのでしょうか 右へ 3 ビットシフトする場合は 次のようになります ビット位置 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 シフト前 1 1 1 0 0 1 1 0 1 0 0 0 1 1 1 0 シフト後 1 1 1 1 1 1 0 0 1 1 0 1 0 0 0 1 図 2-8 算術右シフトこの場合 右の 3 ビット ( 図の黄色部分 ) は こぼれ落ちて なくなります 符号 ( 最左ビット ) は左シフトと同様に 符号がコピーされます 右シフトによってできた 隙間 ( 図の緑部分 ) にも符号がコピーされます なぜ このような面倒なことをするのでしょうか 実際に確認してみましょう 今 話を単純にするために 4 ビットの符号付き数値 ( 第 1 章で説明した ) で考えてみましょう 符号付きですから 最左ビットは符号ビットになります 1 ビット右シフトは 2 で割ることと同じ と説明しましたが これを確かめてみます まず 5 を 2 で割ってみましょう 5 は 0101 ですから 右へ 1 ビットシフトすると 0010 になります これは 2 ですね( 余りは無視 ) - 19-

では -5 を 2 で割ってみましょう -5 はビットでは 1011 になりますから 1 0 1 1 1 1 0 1 1101 となります これは-3 1 ですから 確かにこのシフト方法で良い 2 ことがわかります 余談ですが 負の数値を正の数値で割ったときの答え ( 商 ) と余り ( 剰余 ) はどうなるのでしょうか 7 を 2 で割ると 商 =3 剰余 =1 となりますが これは問題の無いところです では -7 を 2 で割るとどうなるのでしょうか 商 =-3 剰余 =-1 でしょうか 余りが負の数になるのはなんとなくヘン という気がします であれば 商 =-4 剰余 =1 でしょうか もし剰余として負の数値を認めるのであれば 7 を 2 で割る場合も 商 =4 剰余 =-1 としてもよさそうです ちなみにマイクロソフトの Excel で剰余を求めたところ -7 を 2 で割った剰余は 1 となりました ということは商は-4 ということですね Excel で単純に-7/2 としますと 答えが-3.5 となり 少数以下が計算されてしまいます QUOTIENT という関数があるようですが これは商の小数部を切り捨てるという関数ですので ちょっと意味が違ってきます しかし そういえば 切り捨て という動作も考えてみると その値を超えない最大の整数にする のでしょうか それとも ゼロに近い整数にする のでしょうか 3.5 を切り捨てますと 3 になります これは問題ありません では -3.5 を切り捨てるとどうなるのでしょうか 答えは-3 でしょうか でも 越えない最大の整数 という定義をとれば -4 になります 浅学の筆者にはよく分かりませんが 2.3.12 論理シフトシフト命令算術シフト命令の説明で 実は 上図が間違っているのです と書きましたが この間違ったシフト方法が論理シフトです 間違った というと語弊がありますが つまり 符号を意識しない シフトが論理シフトというわけです 左シフト最左ビットを特別なビットとみないで シフトするビット数だけ左へ移動します こぼれたビット はなくなりますし 右側の 空いた ところには無条件に 0 がセットされます 右シフト左シフトと同様に右へシフトされます 左の 空いた 部分には 算術シフトでは符号ビットがコピーされましたが 単に 0 がセットされます 1 1101 の 2 の補数 は 0011 で 3 になります つまり -5 を 2 で割ると商 =-3 剰余 =1 ということです 2 というよりも そうなるようにシフト命令を設計してあるというほうが正確でしょう - 20-

右の こぼれた ビットはなくなります 論理シフトシフトの使い道 論理シフト命令は ある特定のビットが 0 か 1 かを判定する際によく使用されます 例えば 変数 A の左から 3 ビット目が 0 か 1 かを判断する という場合 LD GR1,A ;1 SLL GR1,2 ;2 JMI BITON ;3 1 の命令で変数 A を GR1 へロードします 2 の命令で左へ 2 ビットシフトしますから 最初の ( つまり変数 A の ) 左から数えて 3 ビット目が GR1 の 最左ビットのところへ位置します ビット位置 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 シフト前 1 1 1 0 0 1 1 0 1 0 0 0 1 1 1 0 シフト後 1 0 0 1 1 0 1 0 0 0 1 1 1 0 図 2-9 ビットの判定このビットが 1 であれば ( 負の数値なので ) フラグレジスタ SF が 1 に設定されますから ジャンプ命令で判断をすることができます 上記の3では 負の数値なら ( ビットが ON なら )BITON というところへジャンプする ( 次の命令は BITON から ) ことになります また 上記 2で SLL GR1,3 とすれば 0 か 1 かを判断するビットがなくなってしまいますが フラグレジスタ OF には レジスタから 最後に送り出されたビットの値が設定される と定義されていますので 3 の命令を JOV BITON としても良いことになります つまり 上記 1~3 の命令は 次のように書いても同じということになります LD GR1,A ;1 SLL GR1,3 ;2 JOV BITON ;3 これは右シフトの場合でも同じです 2.3.13 シフト命令命令の注意点 シフト命令の シフトするビット数 は実効アドレスで示されたビット数だけシフトされますが これは 実効アドレスで示されたアドレスの内容分シフトする ということではありませんありません 例を示しましょう - 21-

今 変数 A が 100 番地にあり その内容が 3 であったとします SLL GR1,A とした場合 変数 A の内容が 3 だからといって 3 ビットシフトされるのではありません あくまでも 100 ビットシフトされることになります ( 変数 A が 100 番地なので ) 100 ビット左へ論理シフトすれば GR1 はゼロになってしまいますが このようなわけですから シフト命令のディスプレースメント 1 に変数 ( リロケータブルターム ) を指定することは まず考えられず ほとんどの場合数値そのもの ( アブソリュートターム ) を指定します これは 変数のアドレスが実行時に何番地になるかは予測できないためです (OS がプログラムのロード時に決定します ) ただし インデックスレジスタは使用することがあります シフト数が実行時まで決定できないような場合に SLL GR1,0,GR2 とすれば 実効アドレスは 0+GR2 の内容 になりますから 結果として GR2 の内容分だけシフトされることになります 2.3.14 分岐命令 フラグレジスタの内容により分岐する命令です 次の条件が成立する ( 真 ) ときジャンプし 条件にあてはまらない ( 偽 ) ときはジャンプせず次の命令 へ移ります ジャンプする条件 ジャンプ命令 OF SF ZF JPL 0 0 JMI 1 JNZ 0 JZE 1 JOV 1 JUMP 無条件にジャンプ 表 2-6 フラグレジスタの値と分岐条件 空白部分は 参照しない ということです 従って たとえば JPL 命令は SF と ZF が 0 のときジャンプし これ以外の場合はジャンプしないという ことになります この表を見るときは注意が必要です 1 ディスプレースメント リロケータブルタームについては 4 章を参照してください - 22-

例えば OF=1 SF=0 ZF=0 だった場合 各命令は次のようになります JPL JMI JNZ JZE JOV JUMP SF=0 ZF=0 なのでジャンプする SF=0 なので ジャンプしない ZF=0 なのでジャンプする ZF=0 なのでジャンプしない OF=1 なのでジャンプする ジャンプする 表 2-7 ジャンプ命令命令の意味 分岐命令のオペランドには インデックスを指定することができます これは次のような使用法があり ます JUMP TBL1,GR2 ;1 TBL1 JUMP A1 ;2 JUMP A2 ;3 JUMP A3 ;4 この 1 の JUMP 命令のオペランドは もし GR2 の指定が無く JUMP TBL1 だけであれば 単に TBL1 へジャンプします ( その後 A1 へジャンプすることになりますが ) 今 GR2 に 2 が格納されていた場合はどうなるでしょうか 仮に TBL1 が 100 番地にあったとしますと 1の命令の実行アドレスは 102(100+2) になりますから 102 番地へジャンプします 102 番地はちょうど3の命令のところですから JUMP A2 ;3 が実行されて A2 へジャンプすることになります これは COBOL の GO TO A1 A2 DEPENDING ON XX に似ています 似てはいますが 少し違います 1の命令は GR2 が偶数のときにしかうまく行きません 2や3の JUMP 命令は 2 語の命令 1 になっているからです それでは GR2 が偶数のみでなく奇数の値もとる場合 ( これが普通ですが ) はどうすれば良いでしょうか このような場合は 1のめ異例の前に次のような命令をいれて GR2 を 2 倍してやればよい訳です SLL GR2,1 他にも もっと アセンブラチック な方法がありますが それは第 5 章で 1 2 語と定義されているわけではなく 本書の勝手な設定 詳しくは 4 章参照 - 23-

2.3.15 スタック操作命令 PUSH 命令 PUSH 命令はスタックにデータ ( 実効アドレス ) を 1 件追加する命令です たとえば PUSH KINGAKU とすれば KINGAKU のアドレス (KINGAKU の内容ではありません ) がスタックに追加されます また 例えば GR1 が 3 であるとき PUSH KINGAKU,GR1 とすれば KINGAKU のアドレス (KINGAKU に割り当てられているアドレス )+3 がスタックに追加されます スタックについてはハードウェアの仕様のところで説明しましたが この命令はいったいどのようなときに使用するのでしょうか CALL 命令でも使用されますが これについては CALL 命令のところで説明します まず 思いつく使い方としては サブルーチンにパラメータのアドレスを渡す領域が考えられますが これは RET 命令との関連でうまく行きません ( これについても CALL 命令で説明します ) では 以下で PUSH 命令と POP 命令の両方を説明しましょう しかし その前に POP 命令です POP 命令 POP 命令はスタックから 1 個取り出す命令です PUSH/POP 命令 CALL 命令は主に 外部プログラム を呼び出すための命令です 外部プログラムとは 別にアセンブルしたプログラム と考えて良いでしょう PUSH 命令と POP 命令 外部プログラムではないプログラム ( 内部ルーチン ) を呼ぶ場合に便利なようです この機能は C 言語では定義されていません ( もともと内部ルーチン = 関数の呼び出しばかりで出来ている言語ですが ) が COBOL では PERFORM 命令として定義されています 次のような例を考えてみましょう 今 テストの平均点を求める ことを考えてみます 平均点は 国語と算数の 2 教科について求めるとします 平均点を求めるルーチンを HEIKIN とします - 24-

( 国語の全員分の点数読込 ) PUSH NEXT1 ;1 JUMP HEIKIN ;2 NEXT1 ( 算数の全員分の点数読込 ) PUSH NEXT2 ;3 JUMP HEIKIN ;4 NEXT2 HEIKIN POP GR1 ;5 JUMP 0,GR1 ;6 まず 1 では 内部ルーチン HEIKIN を呼んだあとで HEIKIN からの戻り先として NEXT1 のアドレス を PUSH 命令でスタックへ保存しています そして 2 で HEIKIN へジャンプします HEIKIN では 処理を終えた後 5 の POP 命令でデータを 1 個取り出しますが これは 1 で PUSH した データになりますから NEXT1 のアドレスを取り出すことになります 次 6 のジャンプ命令で NEXT1 へジャンプします ( つまり 2 の次の命令へ戻ってきたことになりま す ) なお この 6 の使い方は分岐命令のところで説明しました さらに 3 と 4 でも同様なことをしていますが この場合では戻り先が NEXT2 になっていますので 5 と 6 で 今度は NEXT2 へ戻ってきます 最初のスタックの状態 NEXT1 のアドレス 1 で 戻り先 NEXT1 をスタックへ PUSH した状態 NEXT1 のアドレス NEXT1 のアドレス 5 で 戻り先 NEXT1 をスタックか ら取り出した状態 - 25-

しかし こんな面倒なことをしないで 単に戻り先アドレスを覚えておくようにしてはどうでしょうか つまり 上のプログラムを次のように変更します ( 国語の全員分の点数読込 ) LAD GR1,NEXT1 ;1 ST GR1,MODORI JUMP HEIKIN ;2 NEXT1 ( 算数の全員分の点数読込 ) LAD GR1,NEXT2 ;3 ST GR1,MODORI JUMP HEIKIN ;4 NEXT2 HEIKIN LD GR1,MODORI ;5 JUMP 0,GR1 ;6 MODORI DS 1 ;7 このプログラムでは 1 で PUSH 命令の替わりに戻りアドレスを 7 の MODORI へ格納しています 3 で も同様です 戻る場合は 戻り先アドレスを POP 命令で取得するのではなく MODORI に保存しておいたアドレスへ 戻っています これでも立派に動作しますが ひとつ問題があります いま HEIKIN からさらに GOUKEI という内部ルーチンを呼ぶとしたらどうなるでしょうか ( 国語の全員分の点数読込 ) LAD GR1,NEXT1 ;1 ST GR1,MODORI JUMP HEIKIN ;2 NEXT1 ( 算数の全員分の点数読込 ) LAD GR1,NEXT2 ;3 ST GR1,MODORI JUMP HEIKIN ;4 NEXT2 HEIKIN LAD GR1,HEI1 ;5 ST GR1,MODORI ;6 JUMP GOUKEI ;7 HEI1 LD GR1,MODORI ;8 JUMP 0,GR1 ;9-26-

GOUKEI LD GR1,MODORI ;10 JUMP 0,GR1 ;11 MODORI DS 1 内部ルーチン HEIKIN では GOUKEI を呼ぶために 5 と 6 で戻りアドレスを MODORI に保存しています そして 7 で GOUKEI へジャンプします GOUKEI では 10 で戻りアドレスを MODORI から取得し 11 で戻ります ( つまり HEI1 へ戻ってきます ) HEIKIN は更にメインへ戻ろうとして 8 を実行しますが このとき MODORI には 6 で格納されたアドレ ス つまり HEI1 のアドレスが格納されています したがって 9 のジャンプ命令では また HEI1 へ戻ってしまいます つまり 8 9 10 11 を繰り返すことになり いつまでたっても終了しません ( 永久ループ ) これを防ぐためにはどうすれば良いでしょうか MODORI を 2 つ用意し そのうち 1 つを HEIKIN から戻るため に 残りを GOUKEI から戻るため に使用 するという考えではどうでしょうか これは良い考えのように思えますが それでは GOUKEI から更に呼び出す必要が出た場合どうなるで しょうか またひとつ用意しなければなりません これではキリが無いですね そこで次のようなことを考えます 戻り先アドレスを格納する領域をたくさん用意し その先頭に RTNADR という名前をつける RTNADR のどこへ格納したかを覚えておくために 変数 RTNPOS を用意し 初期値をゼロにしておく 戻り先を格納するときは RTNADR の RTNPOS 番目へ格納し RTNPOS に +1 しておく 戻るときは RTNPOS から 1 を引き RTNADR の RTNPOS 番目の内容を取り出して そこへ戻る これだとうまく行きそうですが なんだか面倒ですね そこで これを 仕組み として実装したのが PUSH/POP 命令なのです 2.3.16 コール リターンリターン命令 CALL 命令 CALL 命令はサブルーチンを呼び出す命令です これについては 6 章で詳しく説明しますが 次の動作を行います 1 CALL 命令の次のアドレスをスタックへ PUSH し サブルーチンの先頭へ分岐する 2 RET 命令では (CALL 命令でスタックされたアドレスを ) スタックから取り出し その取り出したアド レスへ分岐します RET 命令 CASLⅡ の説明では プログラムの動作を終了する 1 命令として この RET 命令を使用するとされていま す RET 命令は サブルーチンから戻る場合に使用する目的で使用される命令です 少し話が難しくなりますが 1 END 命令がありますが これはプログラム全体の終わりを示すもので プログラムの実行を終了するものではあり ません - 27-

プログラムには メインプログラム ( メインルーチン ) と サブプログラム( サブルーチン ) があります 一般にサブルーチンはメインプログラムから呼ばれて動作するもので サブルーチン自体が独自で実行されることはありません しかし コンピュータ全体を考えてみますと 一番 大元 で動いているのは OS( オペレーティング システム ) です 従って メインプログラムといえども OS から見れば ある種のサブルーチンの位置付けになります というわけで RET 命令を プログラム実行の終了 として使用するとされているわけです OS から呼び出されるとき OS は (OS への ) 戻り先をスタックへ格納してから呼び出します 2.3.17 その他の命令 SVC 命令日本語では スーパーバイザーコール 命令とあり 説明には 実効アドレスを引数として割出しを行う とあります スーパーバイザー 引数 割出し と 難しそうな言葉が出てきました 順に説明します スーパーバイザーコンピュータに処理を行わせるために プログラムを作成し それを実行させますが このプログラムの実行は OS( オペレーティング システム ) が管理しています つまり ユーザーの作成したプログラムは すべて OS の管理下にあるわけで このようなプログラムを プロブレム プログラム(problem program) といいます 我々が作成するプログラムは ほとんどがプロブレム プログラム ( 面倒なので 以下 PP と書きます) ですし コンパイラやアセンブラなど通常はメーカーから提供されるプログラムも やはり PP です ところで コンピュータは通常 OS 制御の下で PP が動いているわけですから (OS もプログラムですから ) 複数のプログラムが同時に動いている 1 ことになります いま PP にバグがあって OS のデータ部分を書き換えてしまうとどうなるでしょうか OS が正しく動作しなくなってしまいます OS はコンピュータの よりどころ ですから OS が動かなくなればコンピュータ自身が止まったり暴走したりすることになります このような事態を防ぐため 通常は PP から OS のデータを参照したり書き換えたりすることはできないような仕組みになっています 無理に参照したり書き換えたりすると アドレス例外 とか プロテクト例外 で PP が止まる仕組みになっています とはいうものの PP から OS のデータを参照したり書き換えたりすることが必要になる場合があります このようなときは PP から直接 OS を参照したり書き換えたりしないで OS 側で用意されたルーチンに処理を依頼することにしています いってみれば 素人がするのではなく プロに依頼する のです この OS 側で用意された ルーチン のことを スーパーバイザー といいます 1 実際は 同時 に動いているわけではなく 非常に短時間で切り替わっているので 同時に感じられるだけです なお PP が動いているときを PP モード OS が動いているときを スーパーバイザーモード といいます - 28-

通常 スーパーバイザーはたくさんの種類があり たとえば タイマーをセットする とか 排他制御をする とかの仕事の種類により 番号が付けられています そして このようにスーパーバイザーを呼び出す ( 仕事を依頼する ) ことを スーパーバイザー コール と言っているのです また スーパーバイザーに仕事を頼んで スーパーバイザーがそれを実行する ことを 割出し といいます CASLⅡではスーパーバイザー コール (SVC 命令 ) では パラメータに実行アドレスを指定することになっており この 実効アドレス は 引数 であるとしています 引数 ( パラメータ ) というのは スーパーバイザーの種類 依頼する仕事に必要な変数などを指しますが 具体的には定義されていません スーパーバイザー自体についても何も書かれていませんので おそらくこの命令を使用することは無いと思われます 割出割出し と 割込割込み この説明書では 割出し という言葉が使われていますが 割込み という言葉を遣う場合も多いようです どちらも interrupt の訳語と思われます 出し と 込み は意味がまったく反対ですが 微妙に似たところもあります たとえば PC から MO などにデータを書く場合 何と言うでしょうか 通常は 書き込む といいますが PC から見て 書き出す ということもあります 似たような言葉で 時間を示す 前 という言葉があります 通常 前 といえば過去のことですが なんとなく 未来 についても使いそうではないですか NOP 命令 No OPeration 命令で 名前のとおり 何もしない 命令です ノップ と発音します 通常はジャンプ命令の特別な形 ( 次の命令へジャンプする ) として定義される場合が多いのですが CASLⅡでは特別に NOP を定義しているようです この命令はいったい何に使用するのでしょうか いくつか例をあげてみます NOP 命令にラベルラベルをつける 3 章で説明しますが CASLⅡではひとつの命令は 1 行に書かなければならず 複数の行に分けて書くことができません たとえば 次のように書きます LABEL1 ADDA ST GR1,TEATE GR1,GOUKEI 1 行目は次のように書くとエラーになります LABEL1 ADDA ; ラベルのみはエラーです GR1,TEATE - 29-

ここで 最初の命令の前に命令をひとつ挿入する必要が生じたとします たとえば 次のように修正する場合です LABEL1 LD ADDA ST GR1,GOUKEI GR1,TEATE GR1,GOUKEI この場合 2 行修正しなければなりません では 次のようにコーディングしてあればどうでしょうか LABEL1 NOP ADDA ST GR1,TEATE GR1,GOUKEI 1 行追加する場合は LABEL1 NOP LD ADDA ST GR1,GOUKEI GR1,TEATE GR1,GOUKEI とすればよく 修正は 1 行ですみます ラベルのついた命令を削除する場合も同じですね IBM のアセンブラでは EQU という命令があります この命令 ( 実行命令ではありませんが ) は いろいろ使い方がありますが ラベルのみの行をコーディ ングする手段としても使用されます たとえば LABEL1 EQU * AH 1,TEATE これに 1 行追加するのは簡単ですね LABEL1 EQU * LH 1,GOUKEI AH 1,TEATE CASLⅡ にはこのような命令が無いので NOP で代用しようというわけです 命令を変更変更する 5 章で説明しますが 実行時に命令を作り出すために使用します 実行時まで処理が決まらない場合 とりあえず NOP として 命令の領域を確保しておき 実行時に 命令を作り出して これを実行するのです 実行時に命令を作るアセンブラならでは 1 ですね 1 REXX などでも可能です - 30-