x86の命令どもを16ビットモードから眺めてます。今回は条件付ジャンプです。x86を含むクラシックなプロセッサでは、数多くの条件フラグを駆使してジャンプするのが普通です。しかしレジスタの依存関係以外に伏線張ると「近代化時」にメンドクセー筈。モダンなRISC-Vなどはフラグ無、条件判断は条件ジャンプ命令自体で判定だよ。
※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら
※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。
条件付きJump
まあそうは言ってもx86(16ビットモード)の練習なので、条件フラグを見るJcc(ccには条件を示すお名前が入る)命令どもを見ていきます。以下のオペコードマップのように1等地の目抜き通りのファーストオペコードの真ん中にドカンと1行16命令が占拠しております。
後に続くのは8ビットのディスプレースメント値です。この命令の次の命令の先頭を基準に8ビット=256バイト範囲に分岐可能。この1バイトの値を符号拡張して16ビット化、もし条件が適合していればIP(インストラクション・ポインタ)に加え合わせてIPを更新することでジャンプを行います。条件があわなければ、フツーのIPは+2されて次の命令を指します。
なお、IPの更新はプログラムからみえる変化ですが、実際のハードウエアからすると「プログラムからは隠されている」プリフェッチ・ポインタの値も更新されることで新規の命令フェッチが再開されます。その際、そこまでえっちらおっちらプリフェッチ・キューにため込まれた(8086の場合6バイト、8088の場合4バイト)命令コードはフラッシュされてしまいます。
ここのJcc命令以外にもいくつか条件付きのJump命令は存在するのですが、そいつらはまた後で。
さて、Jccのccに入る条件共がどのフラグを「見て」ジャンプするのかを一覧表にしてみましたぞ。条件ジャンプが見ることができるフラグは以下の5つです(AFは見れないっす。)
-
- CF、キャリーフラグ
- ZF、ゼロフラグ
- SF、サインフラグ
- OF、オーバーフローフラグ
- PF、パリティフラグ
x86のようなクラシックなプロセッサはほとんどのALU演算命令で漏れなくフラグ操作してくれちゃいます。
Jcc | CF | ZF | SF | OF | PF |
---|---|---|---|---|---|
JB/JNAE/JC | 1 | – | – | – | – |
JBE/JNA | 1 | 1 | – | – | – |
JE/JZ | – | 1 | – | – | – |
JL/JNGE | – | – | ≠OF | ≠SF | – |
JLE/JNG | – | || 1 | ≠OF | ≠SF | – |
JNB/JAE/JNC | 0 | – | – | – | – |
JNBE/JA | 0 | 0 | – | – | – |
JNE/JNZ | – | 0 | – | – | – |
JNL/JGE | – | – | =OF | =SF | – |
JNLE/JG | – | 0 | =OF | =SF | – |
JNO | – | – | – | 0 | – |
JNP/JPO | – | – | – | – | 0 |
JNS | – | – | – | 0 | – |
JO | – | – | – | 1 | – |
JP/JPE | – | – | – | – | 1 |
JS | – | – | 1 | – | – |
上記で、”-“表記されている部分は「見ない」部分です。例えば、JBE/JNA命令の場合、CF(キャリーフラグ)とZF(ゼロフラグ)の2か所に1が書いてあるので、CFとZFの両方(AND条件)に1が立っている場合にジャンプします。
一か所 “||1″などと書かれているのは、ZFが1なら条件成立、||はOR条件のつもりです。SFのところに”≠OF”と書かれているのは、SFとOFの値が異なっていたら、の意味です。
なんかね、条件メンドイです。その上、Jccのccに入るお言葉がまたいろいろ。たとえば最初の行に
JB/JNAE/JC
などと書かれてますが、これは同じ1個のオペコードバイトの命令を3通りの書き方で書いて良いことを示してます。
-
- JB=Jump on Below
- JNAE=Jump on Not Above or Eaual
- JC=Jump on Carry
の意味です。機能としてはキャリーフラグ(CF)が立っていたらジャンプするというだけの命令ですが、BelowとかNot Above or Equalとかいろいろ言えます。
その辺のお言葉の使い方は以下のような感じです。
判定対象 | 用語 |
---|---|
単純フラグ | C/Z/O/P/S |
符号付整数 | LESS/GREATER/EQUAL |
符号無整数 | ABOVE/BELOW/EQUAL |
符号付きだとLESSとかGREATERとか言い、符号なしだとABOVEとかBELOWと言います。ただしEQUALはどっちでも使うっと。なお否定のときはNつけるのは全部共通デス。
今回練習のアセンブリ言語ソース
強力なx86用アセンブラNASM用のソースです。1回のCMP命令で生成した条件フラグを2回の異なる条件ジャンプにかけてます。
segment code ..start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, stacktop test: mov ax, 0x8000 mov dx, 0x0001 cmp ax, dx ja lbl_above jmp fin lbl_above: jng lbl_not_greater jmp fin lbl_not_greater: fin: mov ax, 0x4c00 int 0x21 resb 2048 segment data align=16 resb 1024 * 63 segment stack class=STACK resb 2048 stacktop:
アセンブルして実行
MS-DOS互換で機能強化されたFreeDOS上、以下のステップで上記アセンブラソース jcc.asm から実行可能なオブジェクトファイルを得て実行することができます(nasmとwatcom Cがインストール済であること。)
nasm -f obj jcc.asm wlink name jcc.exe format dos file jcc.obj debug jcc.exe
赤線引いてあるのが、最初のJump on Above命令です。赤線引いたフラグのNZとNCをみて飛んでます。実際の引数は相対値の0x02ですが、デバッガはオフセットアドレスに変換表示してくれているので、トビ先はCS:0019番地であると分かります。ちゃんと飛んでいるようだね。
黄色の方は2つめのJump on Not Greater命令です。アセンブラではJNGと書いているのに、デバッガはJLE(Jump on less or Equal)と表示してます。複数の表記がありえるので、ディスアセンブル表示はデバッガにおまかせです。
条件フラグの組み合わせはメンドイので覚えきれないけれども、テキトーに書いても多分そういう命令はあるので大丈夫?いいのかそんなことで。