Assembly Language チュートリアル
アセンブリ言語の使い方
サーチ…
備考
アセンブリは、人間が読める形式の機械コードの多くで使われている一般的な名前です。当然異なるCPU(Central Processing Unit)間で多くの違いがありますが、単一CPU上では、異なるアセンブラでコンパイルされたAssemblyのいくつかの互換性のない方言が、CPU作成者によって定義された同一のマシンコードに存在することがあります。
あなた自身のアセンブリ問題に関する質問をしたいのであれば、HWとどのアセンブラを使用しているかを明記してください。そうでなければ、あなたの質問に詳細に答えにくいでしょう。
1つの特定のCPUを組み立てることで、異なるCPUの基礎を学ぶのに役立ちますが、すべてのHWアーキテクチャーは細部に大きな違いがあります。そのため、新しいプラットフォーム用のASMの学習は、
リンク:
前書き
アセンブリ言語は、機械語または機械語の人間が読める形式であり、プロセッサ論理が動作するビットおよびバイトの実際のシーケンスである。一般に人間はバイナリ、8進数、16進数よりもニーモニックで読みやすく、プログラミングするのが一般的であるため、人間は通常アセンブリ言語でコードを書いた後、1つ以上のプログラムを使用して、プロセッサが理解できる機械語形式に変換します。
例:
mov eax, 4
cmp eax, 5
je point
アセンブラは、アセンブリ言語プログラムを読み込み、解析し、対応する機械語を生成するプログラムです。標準ドキュメントで定義されている単一言語であるC ++のような言語とは異なり、さまざまなアセンブリ言語が存在することを理解することが重要です。各プロセッサアーキテクチャ、ARM、MIPS、x86などは、異なるマシンコードを持ち、したがって異なるアセンブリ言語を持ちます。さらに、同じプロセッサアーキテクチャに対して複数の異なるアセンブリ言語が存在することがあります。特に、x86プロセッサファミリには、 ガスシンタックス ( gas
はGNUアセンブラ用の実行ファイルの名前)とIntel構文 (x86プロセッサファミリの創始者にちなんで命名)という2つの一般的なフォーマットがあります 。それらは異なっていますが、どちらの構文でも任意のプログラムを書くことができる点で同等です。
一般に、プロセッサの発明者は、プロセッサおよびその機械コードを文書化し、アセンブリ言語を作成する。その特定のアセンブリ言語が唯一のものであることは一般的ですが、言語標準に準拠しようとするコンパイラライターとは異なり、プロセッサの発明者が定義したアセンブリ言語は、通常は必ずしもそうではありません。
プロセッサには一般的に2種類あります。
CISC(複素命令セットコンピュータ):多くの異なる機械語命令を持つことが多い
RISC(Reduced Instruction Set Computers):これとは対照的に、命令はますます少なくなります
アセンブリ言語プログラマの場合、CISCプロセッサは多くの命令を学習することがあるが、特定のタスクに適した命令が多いことがあり、RISCプロセッサはより少なくて簡単な命令を有するが、任意の操作はアセンブリ言語プログラマ同じことをするためのより多くの指示を書くこと。
他のプログラミング言語のコンパイラは、アセンブラを最初に生成し、次にアセンブラを呼び出してマシンコードにコンパイルします。たとえば、 gccはコンパイルの最終段階で独自のガスアセンブラを使用します。作成されたマシンコードは、多くの場合、リンカープログラムによって実行可能にリンクできるオブジェクトファイルに格納されます 。
完全な「ツールチェイン」は、多くの場合、コンパイラ、アセンブラ、リンカで構成されています。そのアセンブラとリンカを直接使用して、アセンブリ言語でプログラムを記述することができます。 GNUの世界では、 binutilsパッケージにはアセンブラとリンカと関連ツールが含まれています。アセンブリ言語プログラミングに専念している人は、 gccやその他のコンパイラパッケージは必要ありません。
小型マイクロコントローラは、純粋にアセンブリ言語で、またはアセンブリ言語と、CまたはC ++などの1つまたは複数の高級言語との組み合わせでプログラムされることが多い。これは、そのようなデバイスのための命令セットアーキテクチャの特定の態様を高級言語で可能であるよりもコンパクトで効率的なコードを書くためにしばしば使用することができ、そのようなデバイスはしばしば限られたメモリおよびレジスタを有するからである。多くのマイクロプロセッサは、内部にマイクロプロセッサを有する汎用コンピュータ以外の装置である組み込みシステムで使用されている。このような組込みシステムの例は、テレビジョン、電子レンジ、および近代自動車のエンジン制御ユニットである。そのようなデバイスの多くは、キーボードやスクリーンを持たないため、プログラマは一般に汎用コンピュータにプログラムを書き込み、 クロスアセンブラを実行します(この種のアセンブラは実行するプロセッサとは異なる種類のプロセッサ用のコードを生成するため)および/またはクロスコンパイラおよびクロスリンカを使用して機械コードを生成することができる。
このようなツールには、コードを生成するプロセッサと同じくらい多様なベンダーが多数存在します。すべてのプロセッサではなく、多くのプロセッサにGNU、sdcc、llvmなどのオープンソースソリューションもあります。
マシンコード
マシンコードは、特定のネイティブマシンフォーマットのデータの用語であり、通常はCPU (中央処理装置)と呼ばれるプロセッサによってマシンによって直接処理されます。
一般的なコンピュータアーキテクチャ( フォンノイマンアーキテクチャ )は、汎用プロセッサ(CPU)、プログラム(ROM / RAM)と処理されたデータと入出力デバイス(I / Oデバイス)の両方を格納する汎用メモリで構成される。
このアーキテクチャーの主な利点は、以前のコンピューター・マシン(マシン構築のハード・ワイヤード・プログラム)や競合するアーキテクチャー(例えば、 ハーバード・アーキテクチャーは、データ)。短所は一般的なパフォーマンスが少し悪化します。長期的に普遍性は柔軟な使用を可能にしました。これは通常、パフォーマンスコストを上回りました。
これはマシンコードとどう関係していますか?
プログラムとデータは、これらのコンピュータに数値としてメモリに保存されます。データからコードを区別する真の方法はないので、すべての数値をメモリにロードした後、オペレーティングシステムとマシンオペレータはCPUヒントを与え、メモリのエントリポイントでプログラムを開始します。その後、CPUはエントリポイントに格納されている命令(数値)を読み込み、それを厳密に処理し、次の数値を次の命令として順次読み込みます。
たとえば、2つの8ビットの数値(8ビットはグループ化され、1バイトに等しく、0-255の範囲内の符号なし整数): 60
201
、Zilog Z80 CPUでコードとして実行されると、2つの命令として処理されますINC a
(レジスタa
値を1だけインクリメントする)とRET
(サブルーチンから戻り、メモリの異なる部分の命令を実行するためにCPUを指し示す)。
このプログラムを定義するために、人間は、例えば16進数の3C C9
(10進数60と201をベース16のエンコーディングで書かれている)のような16進エディタで、いくつかのメモリ/ファイルエディタでそれらの数字を入力できます。これはマシンコードでプログラミングすることです 。
人間にとってCPUプログラミングの作業を容易にするために、 アセンブラプログラムが作成され、次のようなものを含むテキストファイルを読み取ることができます。
subroutineIncrementA:
INC a
RET
dataValueDefinedInAssemblerSource:
DB 60 ; define byte with value 60 right after the ret
バイト16進数シーケンスを出力する3C C9 3C
、ターゲットプラットフォーム固有の任意の追加番号でラップアラウンド:このバイナリのどの部分を実行可能コードにするか、プログラムのエントリポイント(最初の命令)、エンコードされる部分データ(実行不可能)など
プログラマが値60を持つ最後のバイトを "data"としてどのように指定したのかに注目してください。しかし、CPUの観点からは、 INC a
とは何の違いもありません。命令として準備されたバイトをCPUが正しくナビゲートし、データバイトのみを命令のデータとして処理するのは、実行プログラムによって異なります。
そのような出力は、通常、記憶装置上のファイルに格納され、後でOS( オペレーティングシステム - コンピュータ上で既に実行されているコンピュータコードであり、コンピュータで操作するのを助ける )によってロードされ、実行前にメモリに格納されます。プログラムのエントリーポイント。
CPUはマシンコードだけを処理して実行することができますが、ランダムなものであっても、ランダムに処理することはできますが、OSによって検出されて処理される「 クラッシュ 」からI / Oデバイス、またはコンピュータに接続された機密機器の損傷(家庭のコンピュータでは一般的ではありません:))。
同様のプロセスの後には、機械コード(CPUのネイティブ命令)を表すか、または解釈/ハイブリッド言語の場合には一般的なものに変換するソースコード(プログラムの読みやすいテキスト形式)を数値にコンパイルする他の多くの高レベルプログラミング言語が続きます言語固有の仮想マシンコードは、インタプリタまたは仮想マシンによる実行中にネイティブマシンコードにさらにデコードされます。
いくつかのコンパイラは、コンパイルの中間段階としてアセンブラを使用し、ソースを最初にアセンブラ形式に変換し、アセンブラツールを実行して最終的なマシンコードを取得します(GCCの例: gcc -S helloworld.c
を実行してCプログラムhelloworld.c
)。
Hello world for Linux x86_64(インテル64ビット)
section .data
msg db "Hello world!",10 ; 10 is the ASCII code for a new line (LF)
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, 13
syscall
mov rax, 60
mov rdi, 0
syscall
このプログラムを実行するには、 Netwide Assembler 、 nasm
が必要です。なぜなら、このコードは構文を使用するからです。次に、次のコマンドを使用します(コードがhelloworld.asm
ファイルにあると仮定します)。それらはそれぞれ、組み立て、リンク、実行に必要です。
-
nasm -felf64 helloworld.asm
-
ld helloworld.o -o helloworld
-
./helloworld
コードは、Linuxのsys_write
システムコールを使用します。 ここでは、x86_64アーキテクチャー用のすべてのシステムコールのリストを見ることができます。 書き込みと終了のマニュアルページも参照すると、上記のプログラムをC言語に翻訳することができます。これは同じことをしており、はるかに読みやすくなっています:
#include <unistd.h>
#define STDOUT 1
int main()
{
write(STDOUT, "Hello world!\n", 13);
_exit(0);
}
コンパイルとリンク(最初のもの)と実行のためにここでは2つのコマンドが必要です:
-
gcc helloworld_c.c -o helloworld_c
。 -
./helloworld_c
Hello World for OS X(x86_64、Intelシンタックス・ガス)
.intel_syntax noprefix
.data
.align 16
hello_msg:
.asciz "Hello, World!"
.text
.global _main
_main:
push rbp
mov rbp, rsp
lea rdi, [rip+hello_msg]
call _puts
xor rax, rax
leave
ret
アセンブル:
clang main.s -o hello
./hello
ノート:
- OS XのシステムコールAPIは安定しているとは考えられないため、システムコールの使用はお勧めしません。代わりに、Cライブラリを使用します。 ( スタックオーバーフローの問題への参照 )
- インテルは、ワードよりも大きい構造体は16バイトの境界から始めることを推奨しています。 ( インテルのドキュメントを参照 )
- 注文データはレジスタrdi、rsi、rdx、rcx、r8、およびr9を介して関数に渡されます。 ( システムV ABIへの参照 )
Visual Studio 2015でx86アセンブリを実行する
ステップ1 :「 ファイル」 - >「新規プロジェクト」で空のプロジェクトを作成します。
ステップ2 :プロジェクトソリューションを右クリックし、[ 依存関係の構築 ] - > [カスタマイズの構築 ]を選択します。
ステップ3 : ".masm"チェックボックスをオンにします。
ステップ4 :ボタン「OK」を押します。
手順5 :アセンブリファイルを作成し、これを入力します。
.386
.model small
.code
public main
main proc
; Ends program normally
ret
main endp
end main
ステップ6 :コンパイル!