第2章 基本的な演算

この章では、基本的な演算命令の機械語における構文を紹介します。以降の章で使うことになる構文の紹介が主で、この章では章を通して作り上げるサンプルはありません。

CPUは、基本的にレジスタ上の値で演算を行うため、まず、任意のレジスタへ値を格納する方法を紹介し、その後、四則演算・論理演算等の基本的な演算の方法を紹介します。

なお、機械語コードは、オペランドのパターン(オペランドの数や種類)が変わると機械語のバイト列も変化します。この章では、以降の章でも使用するような代表的な構文のみ紹介します。アセンブラの構文も併せて紹介しますので、演算対象が異なる場合など、ここで紹介しない構文については、as_dump等で確認してみてください。

2.1 データ格納命令(mov)(命令4)

汎用レジスタ

まず、一般的にCPU命令で使用するレジスタを紹介します。

CPUが汎用的にデータ格納先として利用できるレジスタが「汎用レジスタ」です。QEMUモニターのinfo registersコマンドで表示されるレジスタの内、リスト2.1のレジスタが汎用レジスタです。

リスト2.1: QEMUモニターのinfo registersで表示されるレジスタ

RAX=0000000000110101 RBX=00000000bef67f02 RCX=0000000000000000 RDX=0000000000000000
RSI=0000000000407180 RDI=00000000bfe9b018 RBP=00000000bff0eb10 RSP=0000000000210000
R8 =00000000bff0e9dc R9 =0000000000000078 R10=00000000bfe6e080 R11=00000000e7dc4657
R12=00000000bef69540 R13=00000000bef69548 R14=00000000bff24b60 R15=00000000bef6a018

RAXからRDI、R8からR15までの計16個のレジスタが並んでいます。これらはそれぞれ64ビットの大きさがあります。なお、一部のレジスタは、64ビット幅の中の一部分にのみアクセスすることが可能です。レジスタの下位32ビットへのアクセスや、さらにその中の下位16ビットへのアクセス、その16ビットの内の上位/下位それぞれ8ビットずつへのアクセスといったことができます。アクセスできるビット幅には名前が付いており、一覧にすると表2.1の通りです。

表2.1: 汎用レジスタ一覧

64bit全体下位32bit下位16bit下位16bitの上位8bit下位16bitの下位8bit
RAXEAXAXAHAL
RCXECXCXCHCL
RDXEDXDXDHDL
RBXEBXBXBHBL
RSPESPSP--
RBPEBPBP--
RSIESISI--
RDIEDIDI--
R8R8D---
R9R9D---
R10R10D---
R11R11D---
R12R12D---
R13R13D---
R14R14D---
R15R15D---

8ビットの場合のみ、16ビットアクセス時の上位側(H)と下位側(L)とで別々にアクセスできます。16ビット/32ビットのアクセスの際は、「32ビット幅の上位16ビット」や「64ビット幅の上位32ビット」というアクセスはできません。

なお、「R8」から「R15」のレジスタ(32ビットにおける「R8D」から「R15D」)は本書では使用しないため特に紹介しません。使い方はその他のレジスタ同様なので、使ってみたい場合は各自でas_dump等から機械語を確認してみてください。

即値をレジスタへ格納する機械語コードを見てみる

レジスタへの値の格納は「mov」命令で行えます。ここでは8ビットの即値*1を8ビットのレジスタへ格納する命令を見てみます。mov命令はバリエーションが多いので、その他の構文は、必要になったらその都度確認することにします。

[*1] 値そのものを示すオペランドです。アセンブラでは先頭に「$」を付けると即値を表すオペランドになります。

例えば「0x12」という8ビット即値を「AL」レジスタへ格納する場合、アセンブラとしては以下のコードになります。

リスト2.2:

mov     $0x12, %al

as_dumpを使って機械語コードを確認してみます。

$ as_dump mov \$0x12,%al

/tmp/tmp.zHlktVD4V2/a.out:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル:

0000000000000000 <.text>:
   0:   b0 12                   mov    $0x12,%al

mov $0x12,%al」は機械語では「b0 12」であることが分かりました。2バイト目の「12」がオペランドで指定した即値「0x12」です。この値を変えれば、ALへ格納する値を変更できます。

また、機械語の1バイト目の「b0」が「ALへ格納する」事を示しており、ここを変更することで別のレジスタへ値を格納できます。

他のレジスタへ格納する命令も見てみる

例えば、CLレジスタ・DLレジスタへ格納する命令のアセンブラと機械語は表2.2の通りです。

表2.2: mov: 即値0x12をレジスタCL・DLへ格納

CLレジスタ
アセンブラmov $0x12, %cl
機械語b1 12
DLレジスタ
アセンブラmov $0x12, %dl
機械語b2 12

機械語の1バイト目に着目すると、ALの時は「b0」、CLの時は「b1」、DLの時は「b2」と、1ずつ増えています。

このように、レジスタをオペランドに取る機械語は、レジスタを示すバイトを1ずつ変化させることで別のレジスタを指定することができ、レジスタには並び順があります。

8ビットレジスタの並び順は他の命令でも共通で以下の通りです。

表2.3: 機械語における8ビットレジスタ並び順

reg_ofsレジスタ
0AL
1CL
2DL
3BL
4AH
5CH
6DH
7BH

reg_ofs」は、レジスタを示すオペランド部分のベース値に対するオフセットを示します。今回の場合ベースは「b0」で、そこにオフセット値を足すことで任意の8ビットレジスタを選択できます。今後、レジスタをオペランドにとる構文の表現に使用します。

構文: mov: 即値(8bit)をレジスタ(8bit)へ格納

以上をまとめると、構文としては表2.4の通りです。

表2.4: 構文4: mov: 即値(8bit)をレジスタ(8bit)へ格納

アセンブラmov 即値(8bit), レジスタ(8bit)
機械語b0+reg_ofs 即値(8bit)

構文: mov: 即値(16bit)をレジスタ(16bit)へ格納

続いて、16ビット即値を16ビットレジスタへ格納する場合も見てみます。

$ as_dump mov \$0x1234,%ax

/tmp/tmp.eTaoyVyqMf/a.out:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル:

0000000000000000 <.text>:
   0:   66 b8 34 12             mov    $0x1234,%ax

機械語「66 b8 34 12」の最後の2バイト「34 12」が即値0x1234を示しています。「0x12」と「0x34」がひっくり返っているのは、Intel命令のバイトオーダーがリトルエンディアン*2であるためです。そして、対象とするレジスタは2バイト目の「b8」が示しています。

[*2] バイトの並び順(バイトオーダー)を、下の位から順に1バイトずつ並べること。

なお、16ビットレジスタの並び順は以下の通りです。

表2.5: 16ビットレジスタ並び順

reg_ofsレジスタ
0AX
1CX
2DX
3BX
4SP
5BP
6SI
7DI

以上をまとめると構文としては以下の通りです。

表2.6: 構文5: mov: 即値(16bit)をレジスタ(16bit)へ格納

アセンブラmov 即値(16bit), レジスタ(16bit)
機械語66 b9+reg_ofs 即値(16bit)

32ビットと64ビットについては、即値をレジスタへ格納する機会が本書では無く、確認方法等もこれまで同様なので得に紹介しません。

ただ、それぞれのレジスタは他の命令で使うことがあるので、並び順だけ他のビットも合わせて紹介しておきます。

表2.7: レジスタの並び順まとめ

reg_ofs64bit32bit16bit8bit
0RAXEAXAXAL
1RCXECXCXCL
2RDXEDXDXDL
3RBXEBXBXBL
4RSPESPSPAH
5RBPEBPBPCH
6RSIESISIDH
7RDIEDIDIBH

2.2 四則演算

それでは、続いて四則演算を機械語で記述する方法を紹介します。

四則演算の命令は、一部を除いて基本的に2つのオペランドを取り、第1オペランドと第2オペランドで演算した結果を第2オペランドのレジスタ(あるいはメモリアドレス先)へ格納するという挙動になります。

分かりやすい代表的な例として、8ビット即値と8ビットレジスタで計算する場合を中心に紹介します。その他のパターンも同様の方法で構文を確認できますので、興味があれば適宜確認してみてください。

和(add)(命令5)

和を計算するのがadd命令です。2つのオペランドを取り、第1オペランドと第2オペランドの和を第2オペランドへ格納します。

例えば、8ビットレジスタALへ即値0x12を加えた結果をALへ格納する場合、アセンブラでは「add $0x12,%al」となり、その機械語コードは以下の通りです。

$ as_dump add \$0x12,%al

/tmp/tmp.Ebxte7x4HM/a.out:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル:

0000000000000000 <.text>:
   0:   04 12                   add    $0x12,%al

「12」は即値で、「04」が対象レジスタを決めるベース値に見えます。

CLレジスタの場合はどうなるのか見てみましょう。

$ as_dump add \$0x12,%cl

/tmp/tmp.t62F0yZWGi/a.out:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル:

0000000000000000 <.text>:
   0:   80 c1 12                add    $0x12,%cl

機械語コードの構文ががらっと変わりました。

実は、「04 XX」という表現は、8ビット即値をALに加算する場合にだけ使えるもので、8ビット即値と8ビットレジスタでの加算命令の「シンタックスシュガー」のようなものです。

表2.8: 構文6: add: 即値(8bit)をALへ加算

アセンブラadd 即値(8bit), %al
機械語04 即値(8bit)

AL以外の8ビットレジスタへ8ビット即値を加算する場合の構文は以下の通りです。

表2.9: 構文7: add: 即値(8bit)をレジスタ(8bit)へ加算

アセンブラadd 即値(8bit), レジスタ(8bit)
機械語80 c0+reg_ofs 即値(8bit)

das_dump逆アセンブルしてみると分かりますが、構文7でALレジスタを使うこともできるようです。

$ das_dump 80 c0 12

/tmp/tmp.NLv4saxnSr/a.bin:     ファイル形式 binary


セクション .data の逆アセンブル:

0000000000000000 <.data>:
   0:   80 c0 12                add    $0x12,%al

ただ、せっかく1バイト減らせる表現があるので、ALレジスタを使う際は構文6の表現を使うほうが良いです。このように、命令によっては特定のパターンのみ命令のバイト数を減らす構文があります。

差(sub)(命令6)

差は「sub」命令です。

和(add)と同様なので、さくっと構文をまとめます。

subもaddと同様にALレジスタでのみ使える構文があります。以下の通りです。

表2.10: 構文8: sub: 即値(8bit)をALレジスタから減算

アセンブラsub 即値(8bit), %al
機械語2c 即値(8bit)

例えば、8ビット即値0x12を8ビットレジスタALから減算する場合、以下のようになります。

表2.11: 例: sub: 0x12をALから減算

アセンブラsub $0x12, %al
機械語2c 12

次に、AL以外でも使える構文です。

表2.12: 構文9: sub: 即値(8bit)をレジスタ(8bit)から減算

アセンブラsub 即値(8bit), レジスタ(8bit)
機械語80 e8+reg_ofs 即値(8bit)

例えば、8ビット即値0x12を8ビットレジスタCLから減算する場合、以下のようになります。

表2.13: 例: sub: 0x12をCLから減算

アセンブラsub $0x12, %cl
機械語80 e9 12

積(imul)(命令7)

オペランドが1つの場合

オペランドが1つの指定ができます。オペランドが1つのパターンで指定できるのはレジスタ名で、例えば8ビットレジスタを指定した場合、「AL * 指定されたレジスタ → AX」という命令になります。暗黙の内にALレジスタとの掛け算を行い、結果を一回り大きい16ビットのAXレジスタへ格納します。単一オペランド時のレジスタ幅に対する挙動を表2.14にまとめます。

表2.14: imul: 単一オペランドの挙動まとめ

レジスタ幅挙動
8ビットAL * オペランド → AX
16ビットAX * オペランド → DX:AX
32ビットEAX * オペランド → EDX:EAX
64ビットRAX * オペランド → RDX:RAX

オペランドが16ビット以上のレジスタの場合、積は上位ビットと下位ビットがDのレジスタとAのレジスタに分けて格納されます。例えば、16ビットの演算で積が「0x1234 5678」であった場合、「0x1234」がDXへ、「0x5678」がAXへ格納されます。

オペランドに8ビットレジスタを指定した場合、構文としては以下の通りです。

表2.15: 構文10: imul: AL * レジスタ(8bit)をAXへ格納

アセンブラimul レジスタ(8bit)
機械語f6 e8+reg_ofs

例えば、ALの二乗を計算するような場合、以下のようになります。

表2.16: 例: imul: AL * ALをAXへ格納

アセンブラimul %al
機械語f6 e8

オペランドが2つの場合

オペランドが2つの場合の挙動は、和や差と同じで「第1オペランドと第2オペランドの演算結果を第2オペランドへ格納」です。

ただし、第1オペランドに即値を使う構文はありません。

例えば、第1オペランドに「AX」を、第2オペランドに「CX」を指定した場合は、「AX * CX」の結果が「CX」へ格納されます。(単一オペランドの時のように「一回り大きなレジスタへ格納」とはなりません。)

構文としては以下の通りです。

表2.17: 構文11: imul: 第1 * 第2オペランド(16bitレジスタ)を第2オペランドへ格納

アセンブラimul レジスタ(16bit,src), レジスタ(16bit,dst)
機械語66 0f af c0+reg_dst_src

reg_dst_src」は「第1オペランド(src)」と「第2オペランド(dst)」のレジスタの組み合わせを示すオフセット値で、以下の通りです。

表2.18: 16ビットレジスタのreg_dst_src一覧

dst/srcAXCXDXBXSPBPSIDI
AX0x000x010x020x030x040x050x060x07
CX0x080x090x0a0x0b0x0c0x0d0x0e0x0f
DX0x100x110x120x130x140x150x160x17
BX0x180x190x1a0x1b0x1c0x1d0x1e0x1f
SP0x200x210x220x230x240x250x260x27
BP0x280x290x2a0x2b0x2c0x2d0x2e0x2f
SI0x300x310x320x330x340x350x360x37
DI0x380x390x3a0x3b0x3c0x3d0x3e0x3f

各列が「第1オペランド(src)」で、各行が「第2オペランド(dst)」を示す表です。

例に挙げた「AX * CXをCXへ格納する」場合、以下のようになります。

表2.19: 例: AX * CXをCXへ格納

アセンブラimul %ax, %cx
機械語66 0f af c8

なお、imul命令のオペランド2つの場合では、第1・第2いずれにも8ビットレジスタを使用する構文はありません。

オペランドが3つの場合

imulはオペランドが3つの指定もできます。その場合、第1オペランドは「即値」、第2・第3オペランドが「レジスタ」での指定となり、「第1オペランドと第2オペランドの演算結果を第3オペランドへ格納」という挙動になります。

なお、第2・第3オペランドのレジスタ幅に応じて第1オペランドで指定できる即値のビット数が異なります。まとめると表2.20の通りです。

表2.20: オペランド3つの場合の即値・レジスタ幅の組み合わせまとめ

第1オペランド(即値)第2・第3オペランド(レジスタ)
8ビット16ビット
8ビット32ビット
8ビット64ビット
16ビット16ビット
32ビット32ビット
32ビット64ビット

例えば、第1オペランドに8ビット即値「0x12」を、第2オペランドに16ビットレジスタ「AX」を、第3オペランドに16ビットレジスタ「CX」を指定した場合、「0x12 * AX」の結果が「CX」へ格納されます。

構文としては以下の通りです。

表2.21: 構文12: imul: 第1オペランド * 第2オペランドを第3オペランドへ格納

アセンブラimul 即値(8bit), レジスタ(16bit,src), レジスタ(16bit,dst)
機械語66 6b c0+reg_dst_src 即値(8bit)

例に挙げた「0x12 * AXをCXへ格納する」場合、以下のようになります。

表2.22: 例: 0x12 * AXをCXへ格納

アセンブラimul $0x12, %ax, %cx
機械語66 6b c8 12

商と剰余(idiv)(命令8)

商と剰余を求める「idiv」命令の場合、「オペランドが1つ」のパターンの命令しかありません。単一オペランドでレジスタを指定することができ、指定したレジスタのビット幅に応じて表2.23の挙動となります。

表2.23: idiv: 指定したレジスタのビット幅に対する挙動まとめ

オペランドレジスタ幅挙動
8ビット「AX/オペランド」の商をALへ剰余をAHへ格納
16ビット「DX:AX/オペランド」の商をAXへ剰余をDXへ格納
32ビット「EDX:EAX/オペランド」の商をEAXへ剰余をEDXへ格納
64ビット「RDX:RAX/オペランド」の商をRAXへ剰余をRDXへ格納

そして、オペランドが8ビットレジスタの場合の構文は以下の通りです。

表2.24: 構文13: idiv: AX/レジスタ(8bit)の商をALへ剰余をAHへ格納

アセンブラidiv レジスタ
機械語f6 f8+reg_ofs

例えば、AX/ALの商をALへ剰余をAHへ格納する場合、以下のようになります。

表2.25: 例: AX/AXの商(1)をALへ剰余(0)をAHへ格納

アセンブラidiv %al
機械語f6 f8

なお、0で割る演算を行った場合、CPUで「0除算」の例外が発生します。本書では例外時のジャンプ先(ハンドラ)の設定を行っていないので、例外発生時の挙動は未定義となりますので、ご注意ください*3

[*3] QEMU上なので試してみても何も被害は無いとは思います。何が書かれているか分からないところへジャンプしてCPUがそこに書かれたバイト列を命令と解釈して突き進んで行きます。

2.3 論理演算

続いて、論理演算の方法をAND・OR・XOR・NOTの4つについて紹介します。

AND(命令9)

オペランドの数は2つのパターンのみで、第1オペランドと第2オペランドの演算結果を第2オペランドへ格納します。

8ビット即値と8ビットレジスタのANDの結果を8ビットレジスタへ格納する場合の構文を紹介します。

第2オペランドに指定するレジスタがALの場合、1バイト少ない機械語表現があり、構文は以下の通りです。

表2.26: 構文14: and: 即値(8bit) AND ALをALへ格納

アセンブラand 即値(8bit), %al
機械語24 即値(8bit)

例えば、8ビット即値0x12とALのANDをALへ格納する場合、以下のようになります。

表2.27: 例: 0x12とALのANDをALへ格納

アセンブラand $0x12, %al
機械語24 12

そして、第2オペランドのレジスタがAL以外でも使える構文は表2.28の通りです。

表2.28: 構文15: and: 即値(8bit) AND レジスタ(8bit)をレジスタ(8bit)へ格納

アセンブラand 即値(8bit), レジスタ(8bit)
機械語80 e0+reg_ofs 即値(8bit)

例えば、即値0x12とレジスタCLのANDをレジスタCLへ格納する場合の機械語コードは表2.29の通りです。

表2.29: 例: 0x12とCLのANDをCLへ格納

アセンブラand $0x12, %cl
機械語80 e1 12

OR(命令10)

or命令もand命令同様、オペランドの数は2つのパターンのみです。

「8ビット即値(第1オペランド) OR 8ビットレジスタ(第2オペランド)」の結果を第2オペランドへ格納する構文を紹介します。

and命令同様に、第2オペランドのレジスタがALの場合には1バイト短い構文があります。

表2.30: 構文16: or: 即値(8bit) OR ALをALへ格納

アセンブラor 即値(8bit), %al
機械語0c 即値(8bit)

例えば、8ビット即値0x12とレジスタALのORをレジスタALへ格納する場合は以下のようになります。

表2.31: 例: 0x12とALのORをALへ格納

アセンブラor $0x12, %al
機械語0c 12

そして、第2オペランドのレジスタがAL以外でも使える構文は表2.32の通りです。

表2.32: 構文17: or: 即値(8bit) OR レジスタ(8bit)をレジスタ(8bit)へ格納

アセンブラor 即値(8bit), レジスタ(8bit)
機械語80 c8+reg_ofs 即値(8bit)

例えば、即値0x12とレジスタCLのORをレジスタCLへ格納する場合の機械語コードは表2.33の通りです。

表2.33: 例: 0x12とCLのORをCLへ格納

アセンブラor $0x12, %cl
機械語80 c9 12

XOR(命令11)

XORもオペランドの数は2つのパターンのみです。

「8ビット即値(第1オペランド) XOR 8ビットレジスタ(第2オペランド)」の結果を第2オペランドへ格納する構文を紹介します。

and命令・or命令同様に、第2オペランドのレジスタがALの場合には1バイト短い構文があります。

表2.34: 構文18: xor: 即値(8bit) XOR ALをALへ格納

アセンブラxor 即値(8bit), %al
機械語34 即値(8bit)

例えば、8ビット即値0x12とレジスタALのXORをレジスタALへ格納する場合は以下のようになります。

表2.35: 例: 0x12とALのXORをALへ格納

アセンブラxor $0x12, %al
機械語34 12

そして、第2オペランドのレジスタがAL以外でも使える構文は表2.36の通りです。

表2.36: 構文19: xor: 即値(8bit) XOR レジスタ(8bit)をレジスタ(8bit)へ格納

アセンブラxor 即値(8bit), レジスタ(8bit)
機械語80 f0+reg_ofs 即値(8bit)

例えば、即値0x12とレジスタCLのXORをレジスタCLへ格納する場合の機械語コードは表2.37の通りです。

表2.37: 例: 0x12とCLのXORをCLへ格納

アセンブラxor $0x12, %cl
機械語80 f1 12

NOT(命令12)

NOTは、オペランドの数は1つのみです。

「オペランドで指定された8ビットレジスタのNOTをオペランドのレジスタへ格納する」場合、構文は以下の通りです。

表2.38: 構文20: not: レジスタ(8bit)のNOTをレジスタ(8bit)へ格納

アセンブラnot レジスタ(8bit)
機械語f6 d0+reg_ofs

例えば、レジスタALのNOTをレジスタALへ格納する場合は以下のようになります。

表2.39: 例: ALのNOTをALへ格納

アセンブラnot %al
機械語f6 d0

2.4 シフト演算(命令13-15)

シフト演算の命令は以下の4つです。

  • SAL: 算術左シフト
  • SAR: 算術右シフト
  • SHL: 論理左シフト
  • SHR: 論理右シフト

全てオペランドの数は2つです。

各命令について、オペランドを使って実際に行われる演算は以下の通りです。

表2.40: 各シフト演算の挙動

sal第1オペランドの回数分、第2オペランドへ2を掛ける
sar第1オペランドの回数分、第2オペランドを2で割る(符号有)
shl第1オペランドの回数分、第2オペランドへ2を掛ける
shr第1オペランドの回数分、第2オペランドを2で割る(符号無)

sal命令とshl命令は挙動が同じで、どちらでアセンブラを書いても同じ機械語が生成されます。そのため、機械語として実質存在するシフト命令は3つです。

なお、シフト命令は、第1オペランドに即値あるいはCLレジスタしか指定できません。

「第1オペランドを即値(8bit)、第2オペランドをレジスタ(8bit)」の場合、構文は以下の通りです。(shlはsalと同一なので省略)

表2.41: 構文21-23: sal/sar/shr: 第1オペランド=即値(8bit)、第2オペランド=レジスタ(8bit)

アセンブラsal 即値(8bit), レジスタ(8bit)
機械語c0 e0+reg_ofs 即値(8bit)
アセンブラsar 即値(8bit), レジスタ(8bit)
機械語c0 f8+reg_ofs 即値(8bit)
アセンブラshr 即値(8bit), レジスタ(8bit)
機械語c0 e8+reg_ofs 即値(8bit)

例えば、ALレジスタに対して即値3でSAL/SAR/SHRを実施する場合の機械語コードは表2.42の通りです。

表2.42: 例: ALに対して即値3でSAL/SAR/SHRを実施

アセンブラsal $3,%al
機械語c0 e0 03
アセンブラsar $3,%al
機械語c0 f8 03
アセンブラshl $3,%al
機械語c0 e0 03
アセンブラshr $3,%al
機械語c0 e8 03

前述の通り、算術左シフトと論理左シフトは挙動は同じなので、機械語としては同じバイナリ列になっています。

2.5 インクリメント/デクリメント(命令16-17)

インクリメント(inc)とデクリメント(dec)は共に取れるオペランドの数は一つのみで、構文も似たような感じです。

8ビットレジスタをインクリメント/デクリメントする場合の構文は以下の通りです。

表2.43: 構文24-25: inc/dec: レジスタ(8bit)をインクリメント/デクリメント

アセンブラinc レジスタ(8bit)
機械語fe c0+reg_ofs
アセンブラdec レジスタ(8bit)
機械語fe c8+reg_ofs

例えば、ALレジスタをインクリメント/デクリメントする場合は以下のようになります。

表2.44: 例: ALをインクリメント/デクリメント

アセンブラinc %al
機械語fe c0
アセンブラdec %al
機械語fe c8