アセンブリ言語 CASL2 比較命令と分岐命令
コンピューターで数を比べるのってどうやってるの?と思ったことはないですか?
人間にとって数と数を比較することは簡単ですが、機械的に比較を実現するにはどういう手順が必要なのか。
今回はアセンブリ言語の比較命令と比較の仕組みについて情報を共有したいと思います。
比較命令
比較命令は、2つの異なる数値を大きい、小さい、または同じかどうかで比較判定する命令です。
また、比較命令は「算術比較」と「論理比較」の2種類の命令が用意されており、算術と論理で数値の扱いに違いがあります。
算術比較
名称 | オペコード | 第1オペランド | 第2オペランド | 第3オペランド |
ComPare Arithmetic | CPA | レジスタ | アドレス or レジスタ | --- |
レジスタ | アドレス | レジスタ |
使用例1: CPA GR0, A
汎用レジスタGR0の値とメモリA番地の値を算術比較せよ。
使用例2: CPA GR0, 1
汎用レジスタGR0の値とメモリ1番地の値を算術比較せよ。
使用例3: CPA GR0, 1, GR1
汎用レジスタGR0の値とメモリ「汎用レジスタGR1の値 + 1」番地の値を算術比較せよ。
使用例4: CPA GR0, GR1
汎用レジスタGR0の値と汎用レジスタGR1の値を算術比較せよ。
論理比較
名称 | オペコード | 第1オペランド | 第2オペランド | 第3オペランド |
ComPare Logical | CPL | レジスタ | アドレス or レジスタ | --- |
レジスタ | アドレス | レジスタ |
使用例1: CPL GR0, A
汎用レジスタGR0の値とメモリA番地の値を論理比較せよ。
使用例2: CPL GR0, 1
汎用レジスタGR0の値とメモリ1番地の値を論理比較せよ。
使用例3: CPL GR0, 1, GR1
汎用レジスタGR0の値とメモリ「汎用レジスタGR1の値 + 1」番地の値を論理比較せよ。
使用例4: CPL GR0, GR1
汎用レジスタGR0の値と汎用レジスタGR1の値を論理比較せよ。
比較方法
比較は、比較する2値を引き算し、計算結果の符号の種類を見ることで実現しています。
例えば、AとBという値を比較する場合、まず A - B を実行します。
その結果、AがBより小さければ答えはマイナスになり、AがBが大きければ答えはプラスになります。
また、AとBが同じなら答えがゼロになります。
- A > B・・・A - B の結果、答えの符号はプラス
- A < B・・・A - B の結果、答えの符号はマイナス
- A = B・・・A - B の結果、答えがゼロ(ゼロは符号なし)
ここで、「A ≧ B」はどうするのか?という疑問が湧いてきますが、このように考えます。
「A > B または A = B」
AがBより大きいかAとBが同じ、と考えることで解決できます。
比較時のオペランドの対応関係
オペランドの対応関係は、第2オペランドに対して第1オペランドがどのくらいの大きさであるか、と捉えます。
例えば、CPA GR0, GR1 のオペランドはどのような対応関係で比較しているのかというと...
GR1(後) に対して GR0(前)のデータが、どのくらい大きな値かを見ています。
- CPA GR0, GR1
・・・GR1に対してGR0が大きいのか小さいのか、または同じなのかを比較
算術比較・論理比較の違い
算術比較と論理比較の違いは、マイナスの数を考慮するか、しないかというところにあります。
算術比較・論理比較は、以下のような違いがあります。
例1: GR0の値が-1 、GR1の値が0のときの比較
① 算術比較の場合- CPA GR0, GR1
GR0 < GR1 --> SF = 1 になります
算術比較の場合、SFが1に変化します。
② 論理比較の場合
- CPL GR0, GR1
GR0 > GR1 --> SF = 0 のままです
算術比較の場合、SFが1に変化しません。
それでは、なぜこのように結果が異なるのか見てみます。
なぜ算術比較・論理比較の結果に違いがあるのか
算術比較・論理比較においてフラグに違いがある理由は、算術と論理では16進数の扱いに違いがあることが関係しているためです。
ここで、10進数「-1」は16進数「FFFF」ですが、この16進数「FFFF」は10進数「65535」でもあります。
- -1 = (FFFF)16
- 65535 = (FFFF)16
CPAは、(FFFF)16を-1としてCPLは、(FFFF)16を65535として扱います。
つまり、CPAとCPLでは以下のような違いがあります。
例2: GR0の値が(FFFF)16 、GR1の値が0のときの比較
① 算術比較の場合- CPA GR0, GR1
大小関係が GR0 < GR1(-1 < 0) ・・・SF = 1
(FFFF)16は、10進数で-1であるため、SFが1に変化します。
② 論理比較の場合
- CPL GR0, GR1
大小関係が GR0 > GR1(65535 > 0) ・・・SF = 0
(FFFF)16は、10進数で65535であるため、SFが0のまま変化しません。
以上のように、似たような比較命令でも真逆の結果になってしまうのですね。
その前に、なぜ、-1と65535が同じになってしまうのか?それについては「16進数のマイナスの数」の記事も参考にしてみてください。
比較の仕組み
コンピュータにおいて比較とは、比較する値と値の減算をハードウェア上で行うことによって実現しています。
すなわち、「第1オペランド - 第2オペランド」 を行いフラグレジスタがどう変化したのかを見て判断しています。
【比較の方法】- 第1オペランド ― 第2オペランド
ここで、「CPA GR0, GR1」 のニーモニックを例に比較してみます。
まず、COMET2は、比較のために減算(GR0 - GR1 )を行います。
その結果...以下①~③のようになります。
① GR0 < GR1になった場合
GR0 < GR1ということは、GR0がGR1より小さいということであり、減算の結果はマイナスのため SF = 1 になります。
【フラグレジスタ】ZF | SF | OF |
0 | 1 | 0 |
② GR0 = GR1になった場合
GR0 = GR1ということは、GR0とGR1が同じということであり、減算の結果はゼロのため ZF = 1 になります。
【フラグレジスタ】ZF | SF | OF |
1 | 0 | 0 |
③ GR0 > GR1になった場合
GR0 > GR1ということは、GR0がGR1より大きいということであり、減算の結果はプラスのため SF = 0、ZF = 0 になります。
【フラグレジスタ】ZF | SF | OF |
0 | 0 | 0 |
このように、大小の比較は、減算によって変化するフラグレジスタの状態を参照することで実現しています。
ただし、比較命令を使う場合は、OF(オーバーフローフラグ)は気にしなくてもOKです。
分岐命令
分岐命令は、フラグレジスタを変化させる命令の後に使用する命令です。
たいていは比較命令(CPA, CPL)の後に使用することが多いですが、フラグレジスタを変化させる命令の後であれば機能します。
実は、CASL2にはフラグレジスタを変化させる命令と変化させない命令の2種類が存在します。
どの命令がフラグレジスタを変化させ、どの命令が変化させないのか、については「CASL2でフラグレジスタを変化させる命令の種類」の記事を参考にしてみてください。
分岐命令の使用に慣れない内は、比較命令とセットで考えることで、解りやすいでしょう。
正分岐
名称 | オペコード | 第1オペランド | 第2オペランド |
Jump on PLus | JPL | ラベル | --- |
アドレス | レジスタ |
使用例1: JPL LOOP
フラグレジスタの ZF = 0 かつ SF = 0 のとき、ラベルLOOPの行に移動せよ。
使用例2: JPL 1, GR1
フラグレジスタの ZF = 0 かつ SF = 0 のとき、メモリ「汎用レジスタGR1の値 + 1」番地の行に移動せよ。
通常は使用例1のように分岐命令(JPL)の後にラベルを指定する使い方をします。
SF(サインフラグ)= 0 かつ ZF(ゼロフラグ)= 0
つまり、数値がプラス(0を除く)のとき、指定した行に分岐します。
数値が、プラスのときに分岐するため、0の時には分岐しません。
しかし、0という数字も符号が有りません。
そこで、JPL命令では0とマイナス数を区別するために ZF = 0 (比較結果がゼロでないとき)という条件も含んで判定されます。
負分岐
名称 | オペコード | 第1オペランド | 第2オペランド |
Jump on MInus | JMI | ラベル | --- |
アドレス | レジスタ |
使用例1: JMI LOOP
フラグレジスタのSF = 1のとき、ラベルLOOPの行に移動せよ。
使用例2: JMI 1, GR1
フラグレジスタのSF = 1のとき、メモリの「汎用レジスタGR1の値 + 1」番地の行に移動せよ。
通常は使用例1のように分岐命令(JMI)の後にラベルを指定する使い方をします。
SF = 1。つまり、数値に符号が付いたとき(マイナスのとき)に指定した行に分岐します。
零分岐
名称 | オペコード | 第1オペランド | 第2オペランド |
Jump on ZEro | JZE | ラベル | --- |
アドレス | レジスタ |
使用例1: JZE LOOP
フラグレジスタのZF = 1のとき、ラベルLOOPの行に移動せよ。
使用例2: JZE 1, GR1
フラグレジスタのZF = 1のとき、メモリの「汎用レジスタGR1の値 + 1」番地の行に移動せよ。
通常は使用例1のように分岐命令(JZE)の後にラベルを指定する使い方をします。
ZF = 1。つまり、数値が0のときに指定した行に分岐します。
非零分岐
名称 | オペコード | 第1オペランド | 第2オペランド |
Jump on NonZero | JNZ | ラベル | --- |
アドレス | レジスタ |
使用例1: JNZ LOOP
フラグレジスタのZF = 0のとき、ラベルLOOPの行に移動せよ。
使用例2: JNZ 1, GR1
フラグレジスタのZF = 0のとき、メモリの「汎用レジスタGR1の値 + 1」番地の行に移動せよ。
通常は使用例1のように分岐命令(JNZ)の後にラベルを指定する使い方をします。
ZF = 0。つまり、数値が0でないのときに指定した行に分岐します。
オーバーフロー分岐
名称 | オペコード | 第1オペランド | 第2オペランド |
Jump on OVerflow | JOV | ラベル | --- |
アドレス | レジスタ |
使用例1: JOV LOOP
フラグレジスタのOF = 1のとき、ラベルLOOPの行に移動せよ。
使用例2: JOV 1, GR1
フラグレジスタのOF = 1のとき、メモリの「汎用レジスタGR1の値 + 1」番地の行に移動せよ。
通常は使用例1のように分岐命令(JOV)の後にラベルを指定する使い方をします。
OF = 1。つまり、数値がオーバーフローしたとき、指定した行に分岐します。
無条件分岐
名称 | オペコード | 第1オペランド | 第2オペランド |
unconditional JUMP | JUMP | ラベル | --- |
アドレス | レジスタ |
使用例1: JUMP LOOP
ラベルLOOPの行に移動せよ。
使用例2: JUMP 1, GR1
メモリの「汎用レジスタGR1の値 + 1」番地の行に移動せよ。
通常は使用例1のように分岐命令(JUMP)の後にラベルを指定する使い方をします。
フラグレジスタは関係なしに、指定した行に分岐します。
分岐命令は、CPA・CPL命令とセットで使用したり、フラグレジスタを変化させる命令の直前に記述されていることが多いです。
さいごに
今回は、比較命令・分岐命令ということで紹介いたしました。
分岐命令の動作に慣れていないうちは解りづらいですが、実際に手を動かしているうちに理解できるようになるので、ゆる~く頑張ってみてください。
それでは次回にお会いしましょう。