手元に実機も実行環境もありませんのでサポート不可です。
文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。
また、一部に半角カナ文字が使用されています。
今回の内容は、最も重要な所です。
まぁ、とはいえ、理解するというより、覚えるだけの内容なんですけどね(^^;
ではでは、これだけは絶対必要(だと思う)命令について説明しましょう。
とその前に用語説明をまずしておきますね。
ニモニック :いわゆる命令そのものですね。
第一オペランド:大抵は代入される側が多いです。
レジスタでもアドレスでも可。
第二オペランド:代入する値を指す事が多いですね。
レジスタでもアドレスでも、値でも可。
但し、セグメントレジスタは使えない。
あと、フラグレジスタの内容について、説明しておきます。
No. |
種類 |
役割 |
1 |
O flag |
オーバーフローフラグというやつです。 算術演算結果がはみ出した時に立ちます。 |
2 |
D flag |
ディレクションフラグというやつです。 これが立っていると、movs/stos/lods/scas/cmpsとかの時に、SI/DIが-1或いは-2されます。 逆に降りていると、+1か+2されます。 つまり、アドレスの参照方向の指定をするものですね。 movsb などの byte なら増減1、movsw などの word なら増減2の意味です。(2012/12/9追記) |
3 |
I Flag |
インタラプトフラグというやつです。 0の時に、割り込みを禁止して、1の時に割り込みを許可するというフラグです。 |
4 |
S Flag |
サインフラグというやつです。 引き算の時に立ちます。 |
5 |
Z Flag |
ゼロフラグというやつです。 演算結果が0の時に1になり、0以外の時に1になります。 算術比較命令で、Equalを調査する時に多用しますね。 |
6 |
A Flag |
補助キャリーフラグというやつです。 使った事は、ないです。というより、いつの間にか変化してる程度で、一般的にはこのフラグの変化は気にしません。 まぁ、精々、算術・論理演算時に、バイト単位計算なら降りて、ワード単位計算なら、バイト単位での桁借りがあった場合だけ立つというやつです。 つまり、ワード単位計算時の、バイト計算用のキャリーフラグ・・・ただの補助という事ですね。 |
7 |
P Flag |
パリティフラグというやつです。 参照したレジスタなどのビットの数が偶数の時に降りて、奇数の時に立ちます。 参照したレジスタなどの各ビットの値が1である個数が偶数の場合に立ち、奇数の場合に降ります。(2012/12/9記述変更) 通信とかである、パリティチェックに通じるものがありますね。 |
8 |
C Flag |
キャリーフラグというやつです。 算術・論理演算時に桁溢れ(或いは桁借り)が生じた時に立ちます。 結構、多用します(特にビットシフト関係と絡んで)。 |
9 |
T Flag |
トラップフラグというやつです。 何だろう。何に使うんだろう・・・私もよく知りません(^^;;;;;; 多分、割り込み絡みだと思うんですけど・・・。 |
ではでは、ここから本講義の中心内容に入りますね。
結構長いですよ(--;
No. |
命令 |
書式例 |
役割 |
01 |
mov |
mov ax,bx mov ax,0000h mov ax,[Word cs:bx] mov [Byte es:di],al mov [Word es:di],0000h mov ds,0a000h |
いわゆる代入命令っていうやつです。 mov ax,bxはax←bxっていう事になります。 moveっていう単語からついた名前なんでしょう。 一番よく使う命令の1つですね。 第一オペランドに代入される側、第二オペランドに代入する値を指定します。 (注:第一オペランドには、値は不可です) (注:第二オペランドには、セグメントレジスタは不可です) フラグには何にも反映されません。 セグメントレジスタを直接操作できる唯一の命令ですね。 (注:pop pushも操作できるけどね) |
02 |
add |
add ax,bx add ax,0123h add ax,[Word cs:bx] add [Byte es:di],al add [Word es:di],0123h |
足し算命令っていうやつです。 add ax,bxはax←ax+bxっていう事になります。 Additionっていう単語が元らしいです。 演算命令ですので、フラグに反映します。 Z O C P A Sが変化しますね。 ちなみに、add ax,1とinc axはおんなじ結果ですけでも、実行時間とバイト数によって、使い分けるのが定石です。大抵は後者を使う事が多いんですけどもね。 大抵の言語の足し算命令はCompileするとADDに置き換わると思います。 |
03 |
sub |
sub ax,bx sub ax,0123h sub ax,[Word cs:bx] sub [Byte es:di],al sub [Word es:di],0123h |
今度は引き算命令ですね。 sub ax,bxはax←ax−bxっていう事になります。 Subtractionっていう単語が元ですね。多分。 フラグはZ O C P A Sが変化します。 わざとでsub ax,0ffffhとするのも楽しいです(^^; |
04 |
inc |
inc ax inc al inc [Byte cs:bx] inc [Word cs:bx] |
これは実は足し算命令なんです。 inc axはax←ax+1っていう事になります。 Incrementからきてるんですね。 フラグはZ O P A Sが変化します。 そうそう。Cが変化しないんですよね。 つまり、演算のはみ出しがわかんないんです。 まぁ、そういう時にはadd ax,1を使うんですけども。 inc axは1バイト命令で、2クロックですけども、Cが不変っていうのは、結構迷惑します。はい。 CでのA++とかPascalでのInc(A)とかというのは、この命令に置き換わる場合もあるんでしょうね。 |
05 |
dec |
dec ax dec al dec [Byte cs:bx] dec [Word cs:bx] |
これ引き算命に入ります。 dec axはax←ax−1っていう事になります。 Decrementからきてるんですね。 フラグはZ O P A Sが変化します。 これもCが不変です。 cxレジスタ以外でカウントをする時に使える命令なのに、C不変はやっぱり痛いのだ。 |
06 |
push |
push ax push ds push al push [Word Ptr 0000h] |
スタック操作をする命令ですね。 ようは、指定されたレジスタなりをスタックに保存します。 popと抱き合わせで利用する事が殆どですね。 フラグは何も変化しません。 |
07 |
pop |
pop ax pop ds pop al pop [Word 0000h] |
これも、スタック操作系の命令です。 フラグは何も変化しません。 pushされた分を戻す、っていうぐらいが殆どですね |
08 |
pushf |
pushf |
フラグレジスタのみをスタックに待避します。 |
09 |
popf |
popf |
フラグレジスタにスタックから進出します。 当然、フラグの内容は破壊(上書き)されます。 |
10 |
pusha |
pusha |
push ax bx cx dx si di bpと等価です。 |
11 |
popa |
popa |
pop bp di si dx cx bx axと等価です。 まぁ、pushaに対応するものと覚えればいいですね。 |
12 |
cmp |
cmp ax,bx cmp ax,0000h cmp [Byte Ptr cs:bx],05h |
算術比較命令っていうやつです。 Compareという単語が元ですね。 フラグは、O S Z A P Cが変化します。 cmp ax,bxはax−bxっていう事になります。 簡単に言えば、結果を破棄する、引き算ですね。 主に、jz/jnz/jb/jbe/ja/jae/jc/jncと抱き合わせで使用します。 |
13 |
jmp |
jmp [DWord cs:bx] jmp 0123h jmp Short 01h |
強制ジャンプっていうやつです。 簡単に言えば、指定されたAddressへ飛べ。 それだけですね。 |
14 |
jz |
jz 7fh |
Z Flagが1の時(立っている時)にジャンプします。 一番よく使う条件分岐命令ですね。 ちなみに、-128〜+127の幅でしかジャンプできません。 所謂「Short Jump」という輩なんですけども、この幅に結構左右される事が多いです。 時と場合によっては、「jnz」と「jmp」でjzの変わりをする時もあるんですよ。 ちなみに、後に述べる全ての条件分岐命令(jz/jnz/ja/jae/jb/jbe/jc/jnc)は「Short Jump」しかできません。 |
15 |
jnz |
jnz 7fh |
jzの対になるやつでして、Z Flagが0の時(降りている時)にジャンプします。 まぁ、それだけですね。 |
16 |
ja |
ja 7fh |
(算術的)演算結果が0よりも大きい場合分岐します。 C FlagとZ Flagが降りている時というのが本当の表現なんですけども。 まぁ、cmp ax,bxとして、AX > BXの場合に分岐すると考える方が妥当ですね。 |
17 |
jae |
jae 7fh |
これは、演算結果が0以上の場合になります。 正確には、C Flagが0の時に分岐するという事になります。 cmp ax,bxの時に、AX >= BXの場合と考えましょう。 或いは、jaにEqualのeがついてjaeになったと覚えるのも手かも。 |
18 |
jb |
jb 7fh |
jaとは対で、0未満の場合に分岐します。 Flagで言えば、C FlagとZ Flagが立っている時です。 cmp ax,bxの時に、AX < BXの時に分岐すると考えてもいいです。 |
19 |
jbe |
jbe 7fh |
jaeの対と考えましょう。ですから、0以下の時に分岐します。 今度は、C Flagが1の場合に分岐します。 |
20 |
jc |
jc 7fh |
C Flagが立っている時に分岐します。 ちなみに、他にもフラグに合わせて、js/jo/jpというのもあります。 |
21 |
jnc |
jnc 7fh |
C Flagが降りている時に分岐します。 ちなみに、他にもフラグに合わせて、jns/jno/jnpというのもあります。 |
22 |
call |
call [Word cs:bx] call [DWord cs:bx] call 0123h |
Procedure(Subroutine)に一時的にジャンプするというような感じです。 他の言語でもよくあるように、Subroutineに一時的に行って、何かしらの動作をして、戻ってくるという時に使います。 正確には、現在のIPの内容(或いはCS:IP)(帰還後に処理すべき内容を保管しているAddress)を保持(スタックに待避)して、retやretfの時に保持した内容(スタック)を参照して帰還してくるようになっています。 BASICのGoSubとか、On -- GoSubなんかは、これを元にして考えられたのではないでしょうか。(2012/12/9この一行破棄) |
23 |
ret |
ret |
callの対みたいなやつです。 つまりは、callで呼び出されたProcedureが上位Procedureに帰還(Return)する時に用いる命令です。 ちなみに、Near Returnとも呼ばれます。 |
24 |
retf |
retf |
retの時に「Near」とつけましたが、これは「Far Return」というやつです。 役割は殆どretと同様なのですが、retはIPのみをpopするのに対し、retfはCS:IPをpopします。 もっといいますと、callで第一オペランドにWord単位を与えた時はretで帰還して、DWord単位を与えた時はretfで帰還するようにするという事です。 |
25 |
int |
int 21h |
Interruptという単語が元になっています。 内容は、User指定の割り込みとか、ソフトウェア割り込みとかと呼ばれています。 ただ、単純にint命令を呼び出した時は、殆どcall DWordと変わりません。変わるとすれば、Flagを保持したりする程度です。 音源ドライバなどで使用されている割り込みは「外部割り込み」とか、「ハードウェア割り込み」とかと呼ばれていまして、ああいうのは、ハードウェア的に勝手に割り込みがかかるようになっているようになっています。 それに対し、この命令はソフトウェア的にかかるもので、殆どProcedure Callするのとかわらないのはその為です。 第一オペランドに与える内容は、割り込みの番号で、Vectorが0000:0000〜0000:03ffまでに登録されており、それぞれ番号に対応して割り込み時にCallされるAddressが保存されています。 まぁ、call [Word ds:si+bx]とかのようにTableを自分で色々と計算して用いるのではなく、CPUが勝手にTableを作っていると考えるべきといいでしょう。(2012/12/9文言差し替え) |
26 |
iret |
iret |
intに対応したものです。 つまり、intで呼び出されたProcdureが上位Procedureに帰還する時に用いる命令です。 Interrupt Returnと覚えましょう。 |
27 |
loop |
loop 07fh |
CXレジスタをカウンタとして用いて、ループをする命令です。 CX=0になるまでずっと、指定されたAddressへジャンプします。 for - nextとか、for to doとか、do - loopとか、do - continueとかに対応する命令ですね。 ちなみに、Shortの枠内になっています。 あと、jcxzという命令もこれに少し関連する命令としてありますが、これは、CX=0の時に分岐するという命令です。 つまり、loopの枠内を越えた場合、dec cx/jcxz ----/jmp ----としてloopをShort以上の幅に拡張する為に存在する命令なんでしょう。 |
28 |
or |
or ax,bx or al,7fh |
論理和というやつですね。 or ax,bxは、AX ← AX ∪ BXになります。 ちなみに、or ax,axとすれば、cmp ax,0と等価で、1 Byteの節約になるんですよ。 |
29 |
and |
and ax,bx and al,7fh |
論理和があれば、論理積ですね。 and ax,bxは、AX ← AX ∩ BXになります。 |
30 |
test |
test ax,bx test al,7fh |
意味合いは結果を残さないandです。フラグに反映するだけなんですね。 多分、算術比較でcmpがあれば、それに対となるように論理比較でtestを作ろう、って事でできたんでしょう。 |
31 |
xor |
xor ax,bx xor al,7fh |
排他的論理和と呼ばれるやつなんですけども、0と0は0、1と1も0、0と01は1という輩です。 ちょっぴりややこしいんですけども、xor ax,axとすれば、mov ax,0と等価で、1 Byteの節約になったりします。 一般にはあんまり利用しませんけど、xor ax,axという方法で多用する命令なんですね、これが。(2012/12/9この1行破棄) |
32 |
neg |
neg ax neg [Byte 0123h] |
80x86系CPUでは、数値は2の補数表現をとっているんですけども、この命令は所謂「補数をとる」というやつです。 もっと言えば、2の補数表現に於ける、符号反転と考えましょう。 |
33 |
xchg |
xchg ax,bx xchg ah,al xchg [Byte 0123h],al |
第一オペランドの内容と第二オペランドの内容を入れ替えるものです。 まぁ、それだけですね。 |
34 |
cbw |
cbw |
Convert Byte to Wordという言葉の略称だと思います。 ALレジスタをAXレジスタに拡張する命令なんですけども、ALの最上位ビットでAHを埋めると考えましょう。 正確には、ALの符号をAHに反映する・・・とでも考えるべきでしょうけども。 ちなみに、cwdというのもありまして、これは、AXをDX:AXにするというやつなんです(Word to Double Wordですね)。 |
35 |
shl |
shl ax,1 shl al,cl shl [Word cs:bx],7 |
シフト命令というやつです。 ビットを左シフトするんですけども、左右の間隔は、前講義内容を参照してください。 ちなみに、この命令では、左にシフトした分だけ、右から0が入ってきます。 あと、clレジスタで、何ビットシフトするかを指定できたり、直接第二オペランドにシフトする数を指定できたりします。 また、シフトではみ出したビットは、C Flagに入ります。 |
36 |
shr |
shr ax,1 shr al,cl shr [Word cs:bx],7 |
これは、右シフトになります。 動作は殆どshlとおんなじですね。 ちなみに、sarは、符号ビットを保持して、シフトします。 つまり、最上位ビットの内容は残りながら、その内容がシフトされていくというものです。 |
37 |
rol |
rol ax,1 rol al,cl rol [Word cs:bx],7 |
Rotate Leftというやつですね。 つまり、左に1ビットずつローテーションするというものなんです。 但し、最上位から最下位ビットへ行く時に、その内容がC Flagに入ります。 尚、rclは、C Flagを含んでRotateします。 |
38 |
ror |
ror ax,1 ror al,cl ror [Word cs:bx],7 |
rolに対して、今度は右にローテーションします。 動作は殆どおんなじです。 これも、rcrは、C Flagを含んでRotateします。 |
39 |
out |
out dx,al out 7eh,ax |
I/O Port命令です。これは、Out Portになります。 I/O Port Addressが1 Byteで表される範囲内の場合だけ、直接そのPortを数字で指定できるようになっています。 それ以外の場合(2 Byteの場合)はDXレジスタを使用します。 また、AL或いはAXで出力する内容を指定します。 |
40 |
in |
in dx,al in 7eh,ax |
今度は、I/O PortのIn Port命令になります。 DX或いは直値でI/O Port Addressを指定して、AX或いはALに内容を保管するようにします。 |
41 |
cld |
cld |
フラグレジスタの内容を直接操作できる命令です。 Direction FlagをClearします。 |
42 |
std |
std |
フラグレジスタの内容を直接操作できる命令です。 Direction FlagをSetします。 |
43 |
clc |
clc |
フラグレジスタの内容を直接操作できる命令です。 Carry FlagをClearします。 |
44 |
stc |
stc |
フラグレジスタの内容を直接操作できる命令です。 Carry FlagをSetします。 |
45 |
cmc |
cmc |
フラグレジスタの内容を直接操作できる命令です。 Carry Flagを反転します。 |
46 |
cli |
cli |
フラグレジスタの内容を直接操作できる命令です。 Interrupt FlagをClearします。 |
47 |
sti |
sti |
フラグレジスタの内容を直接操作できる命令です。 Interrupt FlagをSetします。 |
48 |
nop |
nop |
No Operationという命令です。 3 クロック命令で、1バイトを使用します。 拡張用領域の確保に使用したり、タイミングをとるのに使用したり・・・ 色々と使用したりする命令なんですけども、最近はあんまり気にしない人が多いかな(^^; |
49 |
rep |
rep |
この後に説明する、movs/stos/cmps/lods/scas命令と深い関係のある命令です。 まぁこの命令単体では、cxレジスタが0になるまで、SIとかDIとかが+1されたり、-1されたりするのを助けるだけですね。 |
50 |
repnz |
repnz |
これは、cxレジスタが0でない間というだけで、基本的にrepとおんなじですね。 |
51 |
movsb |
movsb movsw rep movsb repnz movsw |
[ES:DI] ← [DS:SI]という命令です。 bはByte単位、wはWord単位という意味です(後に続くstos/cmps/lods/scasも同義)。 ちなみに、repとかと組み合わせると、cxをカウンタとして、D Flagが1なら、SI/DIは-1か-2され、D Flagが0なら、SI/DIは+1か+2されます(これも、以下は同義)。 |
52 |
stosb |
stosb stosw rep stosb repnz stosw |
[ES:DI] ← AL/AXという命令です。 |
53 |
cmpsb |
cmpsb cmpsw rep cmpsb repnz cmpsw |
cmp [ds:si],[es:di]という命令です。 |
54 |
lodsb |
lodsb lodsw rep lodsb repnz lodsw |
AL/AX ← [DS:SI]という命令です。 |
55 |
scasb |
scasb scasw rep scasb repnz scasw |
cmp [ES:DI],AL/AXという命令です。 |
56 |
mul |
mul bl mul bx mul [Byte cs:bx] |
第一オペランドがByte単位:AX ← AL × 第一オペランドの内容。 第一オペランドがWord単位:DX:AX ← AX × 第一オペランドの内容。 そういう命令です。つまり、掛け算命令というやつですね。 ただ、すごく遅いです。×2とかの場合は、素直にadd ax,axとか、shl ax,1としましょう。 |
57 |
div |
div bl div bx div [Byte cs:bx] |
第一オペランドがByte単位:AL...AH ← AX ÷ 第一オペランドの内容。 第一オペランドがWord単位:DX...AX ← DX:AX ÷ 第一オペランドの内容。 今度は割り算命令ですね。 ちなみに、÷2なんかの場合は、shr ax,1とする方が定石ですよ。 |
58 |
cs: |
cs: |
度々説明の中でも顔を出してましたね(^^; [cs:bx]とかとすると、csをセグメントとして参照するという命令です。 ちなみに、bx/si/diでメモリ参照する時は、セグメントオーバーライド(cs:/ds:/es:/ss:)がないと、勝手にcs:で参照するものとしてしまうんですよ。 |
59 |
ds: |
ds: |
これは、[ds:bx]とかとすると、dsをセグメントとして参照するという命令です。 |
60 |
es: |
es: |
これは、[es:bx]とかとすると、esをセグメントとして参照するという命令です。 |
61 |
ss: |
ss: |
これは、[ss:bx]とかとすると、ssをセグメントとして参照するという命令です。 ちなみに、bp/spはセグメントオーバーライドがないと、勝手にss:で参照してしまいます。 |
61種類もあったりしますが(^^;まぁ、これだけを使えれば、殆どのソフトは組めます。
(現実に私が今までAssemblerで組んだソフトは、上記命令だけしか使ってないです)
あと、ハンドアセンブルは今講義の目的外ですので、ちょっとその辺りの事は省きました。
それと、8086とP5とかPPとかはクロック数(1命令当たりにかかる時間の単位)が全然違うので、残念ながら省きました。
(というより、手元にそこまで詳しい資料はないの(--;高いからf(^^;)
まぁ、PPなんかは上記命令の大抵は数クロックなんだろうけど(movなんかは1クロックらしいけどね)。
取り敢えず、ここではただの基礎だけですから、そういう応用っぽい事はまたおいおいしましょう(^^;
(どうしてもききたいんでしたら、書きますけど)
というより、自分で書籍をかってきましょう(^^;;;
本当は、あと、各命令のある程度ちゃんとした書式と、バイト数も書こうと思ったんですけど、バイト数まで書いたとして、そこまで気にする人は少ないかな?と思って、省きました。
よくない事だというのは、わかってるんですけど、あくまで「命令の基礎知識」という事で(^^;
それに、大体が組めるようになった時点で、既に「どの命令が最も適切か」は自分でおいおい見つけられる筈ですし、それに、プログラミング自体、「経験の世界」が幅きかしてますしね(^^
ちなみに、「8086マシン語秘伝の書」っていう本のニモニック表はとても見易いぞ
今でも売ってるかどうかは定かじゃないけどね。
というより、ニモニック表の見易さに惹かれて買ってしまった、唯一持ってるアセンブラ系書籍だったりする(^^;
次回講義内容は「画面に文字を表示する」です。
今回までの基礎知識だけである程度はカバーできるようにするつもりです。
(足りない点は、おいおいでてくるという事で)
ちなみに、次回辺りから「プログラミング時の豆知識」というのが時々顔を出すようになると思います。
(あと「ちょっとした余談」っていうのも入れようかなぁ(^^;;;)
手元に実機も実行環境もありませんのでサポート不可です。
文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。
また、一部に半角カナ文字が使用されています。