|
目次
アセンブラ本体と資料を入手せよ!
レジスタの種類
数値をレジスタに代入する
全ては四則演算から
インラインアセンブラ入門
次回の予定
アセンブラ本体と資料を入手せよ!
例えばC言語やDelphiなどの高級言語のソースを実行可能プログラムにするためには、コンパイラという機械語に翻訳
してくれるツールが必要になりますよね。それと同様に、アセンブリ言語を機械語に翻訳するためのツールとして
アセンブラが必要になります。まずはそれを入手しましょう。
※Javaの場合は中間言語に翻訳することになるのでチョット違う
とりあえずメジャーなフリーのアセンブラとして次の2つがあります。
MASM32(現在MASM32v7が最新版だね!)
NASM (Nasm distributionを落とせ!)
使い方などに関しましてはそのうちお話します。
ちなみにVisual C++を持っている人は、それを利用してインラインアセンブラという形でアセンブリ言語
を扱うことが可能です。本講座ではインラインアセンブラの利用法はジャンジャン紹介していきますので
楽しみにしていてください。
次に資料の入手です。
Intelの
インテル(R) Pentium(R) 4 プロセッサ関連マニュアルより
IA-32 インテル(R) アーキテクチャ・ソフトウェア・デべロッパーズ・マニュアル、
上巻:基本アーキテクチャ (日本語 PDF ファイル: 7,603KB)
IA-32 インテル(R) アーキテクチャ・ソフトウェア・デべロッパーズ・マニュアル、
中巻:命令セット・リファレンス (日本語 PDF ファイル: 11,688KB)
IA-32 インテル(R) アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル、
下巻:システム・プログラミング・ガイド (日本語 PDF ファイル: 6,526KB)
をダウンロードしてください。基本的にアセンブラの勉強はこれらの資料との格闘であるといっても過言ではありません。
ちなみに「あの〜うちのパソコンPentium 2なんですけど・・・」という方もおられると思いますが(ていうかそれって俺のこと
なんですけど)、ご心配には及びません。Pentium 4といってもPentium 2とかそれ以前のプロセッサから劇的に変化している
訳ではなく、基本的な部分は同じですのでこの資料で問題ありません。
ええ、無いはずですとも!きっと・・・、たぶん・・・、恐らくは・・・、(・・;)おいおい
まぁ、どうしても「Pentium 4のマニュアルなんてイヤだ!俺はPentium Proのマニュアルが欲しいんだ!」とかいう方は、ご自分でIntelの
サイト内を探してください。必ずありますんで。
にしても、Pentium 4のマニュアルは上巻だけで7,603KBもありますね。私同様に低速回線の人にはちとキツイですな。
でもこれが無いことには話にならんのでがんばって落としてくれ!
補足:-------------------------------
中にはプロセッサがIntelじゃない人も当然いますよね。私はIntel以外のことはあまりよく知らないんですけど、
例えばAMDの資料でしたらAMD-K6(R)プロセッサ・ファミリ技術資料
などを参考にするとよいと思います。
これに関してはまだ内容を見ていないのでどんなもんかは知りませんが、基本的に
IntelとAMDには互換性があるとの話をどこぞで聞いた覚えがありますので、本講座で扱う部分に関しましてはどちらのプロセッサであろうと
特に問題は無いと思われます。(一部MMXテクノロジーなどに関してはまたその時に・・・)
目次へ
レジスタの種類
レジスタというのはCPU内のメモリのようなものなのです。演算はメモリ上で直接行うことはできないのでレジスタ
にデータを移動する必要があります。
<汎用レジスタ>
EAX, EBX, ECX, EDX, EBP, ESP, ESI, EDI,
データの格納など汎用的に使える32bitレジスタです。ESI,EDIはメモリを扱う命令で使い、
EBP,ESPは関数呼び出しなどで使います。
<セグメントレジスタ>
CS, DS, SS, ES, FS, GS
メモリへのアクセスに使用するセグメントポインタを格納する16bitレジスタです。
<EIPレジスタ>
プログラムの実行位置を格納する32bitレジスタです。
<EFLAGレジスタ>
様々なステータスフラグが格納された32bitレジスタです。
う〜ん、手抜きだ。近いうちに書き直す。
目次へ
数値をレジスタに代入する
早速にも算術演算の話しを始めたいところですが、まずその前準備としてレジスタへの
数値(即値)の代入について知っておかねばなりません。
基本的にアセンブラで算術演算を行う場合、メモリー上の数値をいったんレジスタに移してから演算処理を
行います。高級言語では A+B のようにメモリー上の変数同士の演算が行えますが、アセンブラではこのようなことは
できません。
そもそも、高級言語でそのような記述をしても、コンパイルをしてアセンブラコードをはいた段階では、
見事にレジスタによる演算に変化しています。ですので、算術演算のニーモニックを覚える前にまずは
値をレジスタに格納する方法を覚えなければならないのです。
ということで、まずは数値を直接レジスタに格納する命令(ニーモニック)を覚えましょう。
以上です。数値をレジスタに格納する命令MOVというのは、move(移動)という意味ですね。上のコード意味を訳すと
「レジスタに数値をMOV(格納)せよ」といったところです。ちなみにレジスタには数値を格納する訳ですから
汎用レジスタを指定しましょう。
実際の例をいくつか見てみましょう。
MOV EAX, 8 (←数値8をEAXレジスタに格納)
MOV EBX, 35 (←35をEBXに格納)
MOV EAX, 110b(←数値の後にbと加えることで2進数として格納できる
o:8進数 h:16進数)
MOV EBX, 'A' (←''で囲むと文字コードを格納できる)
mov eax, 8 (←小文字でも問題無し!)
以上で話しは終了です、という訳にはいきません。何故なら、実際に処理を行う際に
直接レジスタに数値を格納するというだけでなく、メモリ上に格納されている
値をレジスタに移す処理も必要になってくるためです。
ということで、次にメモリからレジスタに数値を格納する場合を考えましょう。
MOV レジスタ, [アドレス] (←アドレスの指定するメモリ上の値を
レジスタに格納)
MOV [アドレス], レジスタ (←レジスタの値をメモリに格納)
メモリにアクセスするにはそのアドレスを指定する必要があります。高級言語では変数を用いて
簡単にメモリにアクセスできますが、アセンブラではアドレスを指定しなければメモリにアクセス
することは出来ないのです。これはC言語に例えるとポインタを用いてメモリにアクセスする方法に相当します。
ではこれも実際の例を見てみましょう。
MOV EAX, [1000h] (←1000h番地に格納されている値をEAXレジスタに格納)
MOV [ff00h], EBX (←EBXの値をff00h番地のメモリに格納)
なお、「アセンブラではアドレスを指定しなければメモリにアクセスすることは出来ない」というのは
ディレクティブ(擬似命令)を使用した場合やインラインアセンブラではこの限りではありません。
これらに関してはまた別の機会にお話しますんでお楽しみに!
とりあえず、今回の話しはこれで終わりです。"とりあえず"というのは、まだ説明し足りない部分が
多々あるためですが、なんかもう疲れちゃったのでこれで終わりです。
説明不足の部分に関してはまた気が向いたときってことで・・・
目次へ
全ては四則演算から
コンピュータとは、それすなわち計算機。という訳でまずは四則演算(加算+、減算−、乗算×、除算÷)を
マスターしましょう。
四則演算の命令はそれぞれ(加算:ADD, 減算:SUB, 乗算:MUL, 除算:DIV)となっています。
それぞれに若干記述方法が違うんで、1つずつ見ていくことにしましょう。
■加算
MOV EAX, 13
MOV EBX, 4
ADD EAX, EBX (←EAXにEBXの値を足しなさい)
■減算
MOV EAX, 13
MOV EBX, 4
SUB EAX, EBX (←EAXからEBXの値を引きなさい)
■乗算
MOV EAX, 13
MOV EBX, 4
MUL EBX (←EAXにEBXの値を掛けなさい)
■除算
MOV EAX, 13
MOV EBX, 4
MOV EDX, 0
DIV EBX (←EAXをEBXの値で輪って、商をEAXに、余りをEDXに入れなさい)
加算と減算は特に説明せずとも理解して頂けると思います。C言語表記でいう所の、
EAX += EBX (つまりは EAX = EAX + EBX)
EAX -= EBX (要するに EAX = EAX - EBX)
に相当する訳です。なお、加算・減算は必ずしもレジスタ同士の演算でなければならない訳ではありません。
メモリーや即値などを指定する事も出来ます。
---------------------------------------------------------
ではちょっとここで、そのことをもう少し詳しく説明するためにと言いますか、アセンブラ関連の
本を読んだときに困らない様にと申しますか、若干横道にそれた話をします。
通常、アセンブラでは ADD EAX, EBX の部分を
という様に呼びます。ニーモニックという言葉は既にこれまで何度か出てきてますが、
いわゆる「命令」です。つまり「〜しろ!」って部分ですね。そして「第1オペランド」「第2オペランド」の
部分はその「命令」に対して与えるパラメータなのです。
各命令によってオペランドに何を指定できるかは違ってきます。また数も2つであるとは限りません。
まだ説明してませんが、乗算にはオペランドが1つしかないでしょ。
また、オペランドがどういう性質のものかによって次のように表現します。(ADDの例)
DESTはdestination(目的地)の略で、SRCはsource(元・源)の略です。SRCはただ参照される
だけですが、DESTには結果が格納されます。つまりはニーモニックの影響を受けるか受けないか
でこのように区別しているわけです。
---------------------------------------------------------
さて、話しを元に戻しましょう。「加算・減算は必ずしもレジスタ同士の演算でなければならない訳ではない」
という話しの続きですが、加算・減算はともにDESTにレジスタ・メモリ、SRCにレジスタ・メモリ・即値(数値)を
指定できます。ただし、メモリ同士の演算は出来ません。
では次に乗算を見てみましょう。
MUL EBX (C言語では EAX *= EBX つまりEAX = EAX * EBX)
あれれ、何か加算・減算の時と違いませんか? MUL EAX, EBX となっていてくれたらもっと
分りやすいってもんですが、なぜゆえにEAXが抜けちゃってるんでしょうか?
実は乗算はEAXレジスタを使用しなければならないという決まりがあるのです。
例えば、加算でしたら、
というように EAXレジスタ 以外のものを利用することも可能ですが、乗算ではそういう訳には
いきません。問答無用でEAXなのです!よって分りきっているEAXの部分は書かんでよし!って
ことで省略、オペランドは1つだけってことになっているのです。
このルールは除算にも適用されます。
MOV EAX, 13
MOV EBX, 4
MOV EDX, 0
DIV EBX (←EAXをEBXの値で輪って、商をEAXに、余りをEDXに入れなさい)
やはり乗算同様オペランドが1つしかないことを確認して頂けると思います。ただ、除算は乗算と違って
計算の結果が1つではありません。「商」と「余り」の2つの演算結果がでてきます。
実はアセンブラでは「商」はEAXレジスタに、「余り」はEDXレジスタに格納すると既に決まっているのです。
これも問答無用です。「いやだ!いやだ!僕は商をEBXに、余りをECXに入れたいんだ!」なんて駄々をこねても無駄です。
だって決まりなんですから。
もちろん演算結果が格納された後で、MOV EBX, EAX と値を移動する分には何の問題もないですよ。
目次へ
インラインアセンブラ入門
以前お話したようにVisual C++では、C言語のソース中にアセンブラコードを組み込むことができます。
そこで今回は前回学んだ四則演算を実際にインラインアセンブラとしてコード中に組み込んでみたいと思います。
ですがその前に、数値をレジスタに代入するの終わり際にお話した
「メモリはレジスタで指定しなきゃいけないけど、インラインの場合はちょっと事情が違うよ!」って話しの
続きをしようと思います。
実はインラインアセンブラではC/C++のデータ型とオブジェクトを参照する事が許されています。
つまりどういうことかと言いますと、
な〜んてことが出来ちゃいます。とても便利で良いのですが、インラインアセンブラだからこそ
出来ないという逆の制約もあります。それに関してはちょっとまだ話していない部分なのですが
アセンブラを既に少し知っている人のために書いときます。
●_asmブロックではMASMのディレクティブや演算子を使ってのデータオブジェクトの定義はできません。
定義ディレクティブ DR,DW,DD,DQ,DT,DF
演算子 DUP, THIS は使用不可
●ついでにMASMの構造体とレコードも使用不可
ディレクティブ STRUC,RECORD,WIDTH,MASK は使用不可
※MSDNを参考というかほぼ引用
では、インラインで変数が使用可能とご理解いただけた所で早速本題に入りましょう!
#include <stdio.h>
int main(int, char**)
{
int a=5;
int b=3;
_asm{
mov eax, a ;アセンブラでのコメントアウトはセミコロン(;)
mov ebx, b //でもインラインの場合「;」と「//」の両方OK!
add eax, ebx
mov a, eax
}
printf("a + b = %d\n", a);
return 0;
}
なんと申しましょうか・・・解説の必要が無いくらい簡単ですね。つまるところ、
というように_asmブロック内にアセンブリコードを埋め込めばよいのです。言うまでも無いと思いますが_asmブロック外で
例えば、printf("a + b = %d\n", eax);なんてしてもエラーですよ。
目次へ
次回の予定
何にしようかな?
目次へ
|