コンピューティングにおいて、アセンブリ言語(別名アセンブラ言語[ 1 ]またはシンボリックマシンコード)[ 2 ] [ 3 ] [ 4 ]は、単にアセンブリと呼ばれることが多く、一般的にASMまたはasmと略されるが、言語の命令とアーキテクチャのマシンコード命令との間に非常に強い対応関係を持つ低レベルプログラミング言語である[ 5 ]。アセンブリ言語は通常、マシンコード命令ごとに1つのステートメント(1:1)を持つが、定数、コメント、アセンブラディレクティブ[ 6 ]、メモリ位置、レジスタ、マクロ[ 7 ] [ 1 ]などのシンボリックラベルも一般的にサポートされている。
機械語命令を表現するために言語が使用された最初のアセンブリコードは、Kathleen BoothとAndrew Donald Boothの 1947 年の著作『Coding for ARC』に見られます。[ 8 ]アセンブリコードは、アセンブラと呼ばれるユーティリティプログラムによって実行可能な機械語に変換されます。「アセンブラ」という用語は、一般的にWilkes、Wheeler、Gillの 1951 年の著書『The Preparation of Programs for an Electronic Digital Computer』[ 9 ]に由来するとされていますが、彼らはこの用語を「複数のセクションからなる別のプログラムを単一のプログラムに組み立てるプログラム」という意味で使用しました。[ 10 ]変換プロセスは、ソースコードのアセンブルのように、アセンブリと呼ばれます。アセンブラがプログラムを処理する際の計算ステップは、アセンブリ時間と呼ばれます。[ 11 ]
アセンブリ言語はマシンコード命令に依存するため、各アセンブリ言語[ nb 1 ]はx86やARMなどの特定のコンピュータアーキテクチャに固有です。[ 12 ] [ 13 ] [ 14 ]
同じアーキテクチャに対して複数のアセンブラが存在する場合もあれば、アセンブラがオペレーティングシステムまたは特定のオペレーティングシステムに固有の場合もあります。ほとんどのアセンブリ言語はオペレーティングシステム呼び出しのための特定の構文を提供しておらず、ほとんどのアセンブリ言語はどのオペレーティングシステムでも普遍的に使用できます。[注2 ]これは、言語がプロセッサのすべての実際の機能へのアクセスを提供し、すべてのシステムコールメカニズムが最終的にその機能に基づいているためです。アセンブリ言語とは対照的に、ほとんどの高級プログラミング言語は一般的に複数のアーキテクチャ間で移植可能ですが、アセンブルよりもはるかに複雑なタスクである解釈またはコンパイルが必要です。
コンピューティングの最初の数十年間は、システムプログラミングとアプリケーションプログラミングの両方が完全にアセンブリ言語で行われるのが一般的でした。一部の用途では今でもアセンブリ言語は不可欠ですが、現在ではプログラミングの大部分はより高水準のインタプリタ型言語とコンパイル型言語で行われています。「No Silver Bullet」の中で、フレッド・ブルックスはアセンブリ言語プログラミングからの移行の影響を次のように要約しています。「ソフトウェアの生産性、信頼性、およびシンプルさにとって最も強力な一手は、プログラミングに高水準言語を徐々に使用するようになったことでしょう。ほとんどの観察者は、この発展によって生産性が少なくとも5倍になり、それに伴い信頼性、シンプルさ、理解しやすさも向上したと考えています。」[ 15 ]
今日では、パフォーマンス上の理由や、高水準言語ではサポートされていない方法でハードウェアと直接やり取りするために、高水準言語で実装された大規模システム内で少量のアセンブリ言語コードを使用することが一般的です。たとえば、Linuxカーネルのバージョン4.9のソースコードのうち、アセンブリ言語で書かれているのは2%弱で、97%以上はC言語で書かれています。[ 16 ]
アセンブリ言語は、低レベルのマシン命令(オペコード)、ディレクティブ、および通常はアーキテクチャレジスタとフラグを表すためにニーモニックシンボルを使用します。[ 17 ]ニーモニックの中には組み込みのものもあれば、ユーザー定義のものもあります。多くの操作では、完全な命令を形成するために1 つ以上のオペランドが必要です。ほとんどのアセンブラは、プログラムとメモリの位置に名前付き定数、レジスタ、ラベルを許可し、オペランドの式を計算できます。そのため、プログラマは面倒な繰り返し計算から解放され、アセンブラプログラムはマシンコードよりもはるかに読みやすくなります。アーキテクチャによっては、これらの要素は、固定アドレスだけでなくオフセットやその他のデータを使用して、特定の命令やアドレッシング モードに組み合わせることもできます。多くのアセンブラは、プログラム開発を容易にし、アセンブリ プロセスを制御し、デバッグを支援する追加のメカニズムを提供します。
列指向型のアセンブラもあり、特定のフィールドが特定の列に配置されます。これは、1950年代から1960年代初頭にかけてパンチカードを使用するマシンで非常に一般的でした。自由形式の構文を持つアセンブラもあり、フィールドは句読点や空白などの区切り文字で区切られます。ハイブリッド型のアセンブラもあり、ラベルは特定の列に配置され、その他のフィールドは区切り文字で区切られます。これは、1960年代には列指向型の構文よりも一般的になりました。
アセンブラプログラムは、操作とアドレッシングモードのニーモニックと構文の組み合わせを数値に変換してオブジェクトコードを作成します。この表現には通常、操作コード(「オペコード」)とその他の制御ビットおよびデータが含まれます。アセンブラはまた、定数式を計算し、メモリ位置やその他のエンティティのシンボル名を解決します。 [ 22 ]シンボル参照の使用はアセンブラの重要な機能であり、面倒な計算やプログラム変更後の手動アドレス更新を省きます。ほとんどのアセンブラには、テキスト置換を実行するためのマクロ機能も含まれています。たとえば、サブルーチンを呼び出す代わりに、一般的な短い命令シーケンスをインラインで生成します。
アセンブラの中には、命令セット固有の単純な最適化を実行できるものもあります。その具体的な例として、様々なベンダーから提供されている広く普及している x86 アセンブラが挙げられます。ジャンプサイジングと呼ばれるこれらのアセンブラのほとんどは、要求に応じて任意の回数のパスでジャンプ命令の置換(長いジャンプを短いジャンプまたは相対ジャンプに置き換える)を実行できます。RISCアーキテクチャ向けのアセンブラの中には、 CPU パイプラインを可能な限り効率的に活用するための適切な命令スケジューリングを最適化するのに役立つものなど、命令の単純な再配置や挿入を行うものもあります。 [ 23 ]
アセンブラは1950年代から存在し、機械語の一歩先を行くものであり、Fortran、Algol、COBOL、Lispといった高級プログラミング言語よりも前の段階に位置づけられてきました。また、アセンブリ言語と高級言語の両方の特性を併せ持つ翻訳ツールや半自動コード生成ツールも数多く存在し、 Speedcodeはその中でも特に有名な例の一つと言えるでしょう。
特定のCPUや命令セットアーキテクチャに対して、構文の異なる複数のアセンブラが存在する場合があります。例えば、x86ファミリープロセッサでメモリデータをレジスタに追加する命令は、オリジナルのIntel構文では となりますが、 GNUアセンブラで使用されるAT&T構文では となります。見た目は異なりますが、異なる構文形式は一般的に同じ数値マシンコードを生成します。また、単一のアセンブラでも、構文形式のバリエーションや正確な意味解釈をサポートするために、複数のモードを持つ場合があります(x86アセンブリプログラミングの特殊なケースでは、FASM構文、TASM構文、理想モードなど)。add eax,[ebx]addl (%ebx),%eax
オブジェクトファイルを生成するためにソースコードを何回読み込む必要があるか(アセンブラがソースコードを何回読み込むか)に基づいて、アセンブラには2種類あります。
どちらの場合も、アセンブラは後続のシンボルのアドレスを計算するために、最初のパスで各命令のサイズを決定できなければなりません。つまり、後で定義されたオペランドを参照する演算のサイズがオペランドの型や距離に依存する場合、アセンブラは演算に最初に遭遇したときに悲観的な見積もりを行い、必要に応じて、後のパスまたはエラッタで 1 つ以上の「何もしない」命令でそれをパディングします。ピーホール最適化を備えたアセンブラでは、パス間でアドレスが再計算され、悲観的なコードをターゲットからの正確な距離に合わせて調整されたコードに置き換えることができます。
ワンパスアセンブラを使用する本来の理由は、メモリサイズとアセンブリ速度でした。多くの場合、2回目のパスではシンボルテーブルをメモリに格納し(前方参照を処理するため)、テープ上のプログラムソースを巻き戻して再読み込みするか、カードデッキまたはパンチ紙テープを再読み込みする必要がありました。メモリがはるかに大きい(特にディスクストレージ)後のコンピュータでは、そのような再読み込みなしで必要なすべての処理を実行するスペースがありました。マルチパスアセンブラの利点は、エラータがないため、リンク処理(またはアセンブラが実行可能コードを直接生成する場合はプログラムのロード)が高速になることです。[ 24 ]
例:以下のコードスニペットでは、1パスアセンブラはステートメントS2をアセンブルする際に後方参照BKWDのアドレスを特定できますが、分岐ステートメントS1をアセンブルする際に前方参照FWDのアドレスを特定できません。実際、FWDは未定義である可能性があります。2パスアセンブラはパス1で両方のアドレスを特定するため、パス2でコードを生成する際にそれらのアドレスが既知となります。
S1 B FWD ... FWD EQU * ... BKWD EQU * ... S2 B BKWD
より高度な高水準アセンブラは、次のような言語抽象化機能を提供します。
詳細については、下記の「言語設計」をご覧ください。
アセンブリ言語で書かれたプログラムは、一連のニーモニックプロセッサ命令とメタステートメント(宣言的操作、ディレクティブ、擬似命令、擬似操作、擬似オプなどと呼ばれる)、コメント、およびデータで構成されます。アセンブリ言語命令は通常、オペコードニーモニックに続いてオペランド(データ、引数、またはパラメータのリスト)で構成されます。 [ 26 ] 一部の命令は「暗黙的」である場合があり、これは命令が操作するデータが命令自体によって暗黙的に定義されていることを意味します。このような命令はオペランドを取りません。結果として得られるステートメントは、アセンブラによってメモリにロードして実行できる機械語命令に変換されます。
例えば、以下の命令は、x86 / IA-32プロセッサに8 ビットの即値をレジスタに移動するように指示します。この命令のバイナリ コードは 10110 で、その後に使用するレジスタの 3 ビットの識別子が続きます。ALレジスタの識別子は000 なので、次のマシン コードはALレジスタにデータ 01100001をロードします。 [ 26 ]
10110000 01100001
このバイナリコンピュータコードは、以下のように16進数で表現することで、より人間が読みやすい形式にすることができます。
B0 61
ここで、B0は「次の値のコピーをAL レジスタに移動する」という意味で、 は6101100001 の 16 進数表現であり、10 進数では 97 です。8086 ファミリのアセンブリ言語では、このような命令に対してMOV ( moveの略) というニーモニックが 用意されているため、上記の機械語コードは、必要に応じてセミコロンの後に説明コメントを追加して、アセンブリ言語で次のように記述できます。この方がはるかに読みやすく、覚えやすいです。
MOV AL , 61h ; ALに10進数97(16進数61)をロードする一部のアセンブリ言語(この言語を含む)では、MOV などの同じニーモニックが、即値、レジスタ内の値、レジスタ内の値または命令に埋め込まれた直接アドレスによって指されるメモリ位置など、データのロード、コピー、移動を行う一連の関連命令に使用される場合があります。他のアセンブラでは、L を「メモリをレジスタに移動」、ST を「レジスタをメモリに移動」、LR を「レジスタをレジスタに移動」、MVI を「即値オペランドをメモリに移動」など、個別のオペコードニーモニックを使用する場合があります。
異なる命令に同じニーモニックが使用される場合、それは、61hニーモニックに続くオペランドに応じて、データ(この例では )を除いて、ニーモニックが複数の異なるバイナリ命令コードに対応することを意味します。たとえば、x86/IA-32 CPU の場合、Intel アセンブリ言語構文は、レジスタAHの内容をレジスタALMOV AL, AHに移動する命令を表します。[ nb 3 ]この命令の 16 進数形式は次のとおりです。
88 E0
最初のバイトである88hは、バイトサイズのレジスタと別のレジスタまたはメモリとの間の移動を識別し、2番目のバイトであるE0hは、両方のオペランドがレジスタであり、ソースがAH、宛先がALであることを指定するために(3つのビットフィールドで)エンコードされます。
このように、同じニーモニックが複数のバイナリ命令を表す場合、アセンブラはオペランドを調べて生成する命令を決定します。最初の例では、オペランドは61h有効な16進数値定数であり、有効なレジスタ名ではないため、B0命令のみが適用可能です。2番目の例では、オペランドAHは有効なレジスタ名であり、有効な数値定数(16進数、10進数、8進数、または2進数)ではないため、命令のみが88適用可能です。
アセンブリ言語は、このような曖昧さの排除が構文によって普遍的に強制されるように設計されています。例えば、Intel x86アセンブリ言語では、16進定数は数字で始まる必要があり、16進数「A」(10進数の10に相当)は、レジスタAHの名前と間違えられないように、またはと記述0Ahされます。(同じ規則は、レジスタBH、CH、DHの名前、および文字Hで終わり、それ以外は16進数のみで構成されるユーザー定義シンボル(例えば「BEACH」という単語)についても曖昧さを防止します。)0AHAH
元の例に戻ると、x86 オペコード 10110000 ( B0) は 8 ビットの値をALレジスタにコピーしますが、10110001 ( B1) はそれをCL レジスタに移動し、10110010 ( B2) はそれをDL レジスタに移動します。これらのアセンブリ言語の例を以下に示します。[ 26 ]
MOV AL , 1h ; ALに即値1をロードMOV CL , 2h ; CLに即値2をロードMOV DL , 3h ; DLに即値3をロードMOV の構文は、以下の例に示すように、より複雑になる場合もあります。[ 27 ]
MOV EAX , [ EBX ] ; EBX に格納されているアドレスのメモリ内の 4 バイトを EAX に移動MOV [ ESI + EAX ], CL ; CL の内容を ESI+EAX のアドレスの 1 バイトに移動MOV DS , DX ; DX の内容をセグメント レジスタ DS に移動いずれの場合も、MOV ニーモニックはアセンブラによってオペコード 88-8C、8E、A0-A3、B0-BF、C6、C7 のいずれかに直接変換され、プログラマは通常、どれが変換されるかを知る必要も覚えておく必要もありません。[ 26 ]
アセンブラはアセンブリ言語を機械語に変換し、逆アセンブラは少なくとも部分的にその逆の変換を行うことができます。高級言語とは異なり、多くの単純なアセンブリ命令と機械語命令の間には1対1の対応関係があります。ただし、場合によっては、アセンブラは、一般的に必要とされる機能を提供するために、複数の機械語命令に展開される擬似命令(実質的にはマクロ)を提供する場合があります。たとえば、「以上であれば分岐」命令がないマシンに対して、アセンブラは、マシンの「未満であればセット」と「(セット命令の結果に基づいて)ゼロであれば分岐」に展開される擬似命令を提供する場合があります。ほとんどのフル機能アセンブラは、ベンダーやプログラマがより複雑なコードやデータシーケンスを生成するために使用する、豊富なマクロ言語(後述)も提供しています。アセンブラ環境で定義された擬似命令やマクロに関する情報はオブジェクトプログラムには含まれていないため、逆アセンブラはマクロや擬似命令の呼び出しを再構築することはできず、アセンブラがそれらの抽象的なアセンブリ言語エンティティから生成した実際の機械語命令のみを逆アセンブルできます。同様に、アセンブリ言語ソースファイル内のコメントはアセンブラによって無視され、生成されるオブジェクトコードに影響を与えないため、逆アセンブラはソースコメントを完全に復元することはできません。
コンピュータのアーキテクチャごとに、独自の機械語が存在します。コンピュータは、サポートする演算の種類と数、レジスタのサイズと数、そしてストレージ内のデータ表現方法において異なります。ほとんどの汎用コンピュータは基本的に同じ機能を実行できますが、その実行方法は異なります。対応するアセンブリ言語は、これらの違いを反映しています。
単一の命令セットに対して、複数のニーモニックまたはアセンブリ言語構文が存在する場合があり、通常は異なるアセンブラプログラムで実装されます。このような場合、最も一般的に使用されるのは、CPUメーカーが提供し、そのドキュメントで使用されているものです。
2 つの異なるニーモニック セットを持つ CPU の 2 つの例として、Intel 8080 ファミリーと Intel 8086/8088 が挙げられます。Intel はアセンブリ言語のニーモニックの著作権を主張していたため (少なくとも 1970 年代から 1980 年代初頭に発行されたドキュメントの各ページに記載)、Intel 命令セットと互換性のある CPU を独自に製造していた一部の企業は、独自のニーモニックを考案しました。Intel 8080Aの拡張版であるZilog Z80 CPU は、8080A のすべての命令に加えて、さらに多くの命令をサポートしています。Zilog は、新しい命令だけでなく、8080A のすべての命令に対しても、まったく新しいアセンブリ言語を考案しました。たとえば、Intel がさまざまなデータ転送命令にMOV、MVI、LDA、STA、LXI、LDAX、STAX、LHLD、SHLDというニーモニックを使用しているのに対し、Z80 アセンブリ言語では、それらすべてにLDというニーモニックを使用しています。同様の例として、 Intel 8086と8088の強化版であるNEC V20およびV30 CPUが挙げられます。ZilogのZ80と同様に、NECはIntelの著作権侵害の疑いを避けるために、8086および8088のすべての命令に対して新しいニーモニックを考案しました。(このような著作権が有効かどうかは疑問であり、後にAMD [注4 ]やCyrixなどのCPUメーカーは、許可も法的制裁も受けずにIntelのx86/IA-32命令ニーモニックをそのまま再公開しました。)実際には、V20およびV30をプログラミングした多くの人が、Intelのアセンブリ言語ではなくNECのアセンブリ言語で記述したかどうかは疑わしいです。同じ命令セットアーキテクチャ用の2つのアセンブリ言語は同型であるため(英語とピッグラテン語のようなもの)、メーカーが公開したアセンブリ言語をそのメーカーの製品で使用する義務はありません。
「Hello, world!」は、オペレーティングシステムの助けをほとんど借りずに、 x86プロセッサ用の32ビットアセンブリ言語で出力できます。「call outchr」は、ALで記述された文字をコンソールに出力するメカニズムを呼び出します。長さがゼロでない文字列は、ゼロバイトで終端する必要があります。
hello: mov esi , msg ; 文字列のアドレスを ESI に格納cld ; ESI をインクリメントする方向を設定lodsb ; 最初の文字を AL にロードし、ESI をインクリメントchrlp: call outchr ; 文字を AL に出力lodsb ; 次の文字を AL にロードし、ESI をインクリメントor al , al ; ゼロ終端文字か? bne chrlp ; そうでない場合は続行ret ; 呼び出し元に戻るmsg: db 'Hello, world!' , 0xa , 0x0 ; 出力する文字列x86プロセッサ上のLinux向け32ビットアセンブリ言語では、「Hello, world!」は単一のオペレーティングシステムコールで出力されます。
section .text ; コードセグメントの開始global _start ; 生成されたオブジェクトファイルで _start が可視となるように宣言_start: mov edx , len ; 文字列の長さ、write() の 3 番目の引数mov ecx , msg ; 文字列のアドレス、write() の 2 番目の引数mov ebx , 1 ; ファイルディスクリプタ (標準出力)、write() の 1 番目の引数mov eax , 4 ; write() のシステムコール番号int 0x80 ; システムコールトラップmov ebx , 0 ; exit() の終了コード、exit() の 1 番目の引数mov eax , 1 ; exit() のシステムコール番号int 0x80 ; システムコールトラップsection .data ; データセグメントの開始msg db 'Hello, world!' , 0xa ; 出力する文字列len equ $ - msg ; アセンブリ時に計算された定数としての文字列の長さアセンブラの開発者によって、命令文の分類方法や使用する用語には大きなばらつきがあります。特に、機械語ニーモニックや拡張ニーモニック以外の命令文を擬似命令(pseudo-op)と呼ぶものもあります。典型的なアセンブリ言語は、プログラム操作を定義するために使用される3種類の命令文で構成されています。
アセンブリ言語の命令(ステートメント)は、一般的に高級言語の命令とは異なり、非常に単純です。一般的に、ニーモニックは単一の実行可能な機械語命令(オペコード)のシンボル名であり、各機械語命令には少なくとも 1 つのオペコード ニーモニックが定義されています。各命令は通常、操作またはオペコードと 0 個以上のオペランドで構成されます。ほとんどの命令は、単一の値または値のペアを参照します。オペランドは、即値(命令自体にコード化された値)、命令で指定または暗黙的に指定されるレジスタ、またはストレージ内の別の場所に配置されたデータのアドレスのいずれかです。これは、基盤となるプロセッサ アーキテクチャによって決定されます。アセンブラは、このアーキテクチャの動作を反映しているだけです。拡張ニーモニックは、オペコードと特定のオペランドの組み合わせを指定するためによく使用されます。たとえば、System/360 アセンブラは、マスクが 15 の場合のB拡張ニーモニックとして、マスクが 0 の場合の拡張ニーモニックとして(「NO OPeration」 - 1 つのステップで何も実行しない) を使用します。BCNOPBC
拡張ニーモニックは、命令の特殊な用途をサポートするためによく使用され、多くの場合、命令名からは明らかでない目的のために使用されます。たとえば、多くの CPU には明示的な NOP 命令はありませんが、その目的で使用できる命令があります。8086 CPU では、命令はに使用され、 は命令 をエンコードするための擬似オペコードです。一部の逆アセンブラはこれを認識し、命令を としてデコードします。同様に、 System/360およびSystem/370用の IBM アセンブラは、と に拡張ニーモニックとをゼロマスクとともに使用します。SPARC アーキテクチャでは、これらは合成命令として知られています。[ 28 ]xchgax,axnopnopxchgax,axxchgax,axnopNOPNOPRBCBCR
一部のアセンブラは、2 つ以上のマシン命令を生成する単純な組み込みマクロ命令もサポートしています。たとえば、一部の Z80 アセンブラでは、命令はに続いてld hl,bcを生成すると認識されます。[ 29 ]これらは擬似オペコードと呼ばれることもあります。ld l,cld h,b
ニーモニックは任意の記号です。1985年にIEEEは、すべてのアセンブラで使用される統一されたニーモニックのセットに関する標準694を発表しました。[ 30 ]この標準はその後撤回されました。
データや変数を格納するデータ要素を定義するために使用される命令があります。これらの命令は、データの型、長さ、およびアライメントを定義します。また、これらの命令は、データが外部プログラム(別々にアセンブルされたプログラム)から利用可能か、データセクションが定義されているプログラムのみから利用可能かを定義することもできます。一部のアセンブラでは、これらを擬似命令として分類しています。
アセンブリディレクティブは、擬似オペコード、擬似操作、または擬似オプとも呼ばれ、アセンブラに「命令のアセンブリ以外の操作を実行するように指示する」コマンドです。[ 22 ]ディレクティブはアセンブラの動作に影響を与え、「オブジェクトコード、シンボルテーブル、リストファイル、および内部アセンブラパラメータの値に影響を与える可能性があります」。擬似オペコードという用語は、データ生成など、オブジェクトコードを生成するディレクティブに限定して使用される場合もあります。[ 31 ]
擬似命令の名前は、機械語命令と区別するためにドットで始まることが多い。擬似命令を使うと、プログラムのアセンブリをプログラマーが入力するパラメータに依存させることができるため、1つのプログラムを異なる方法で、例えば異なるアプリケーション向けにアセンブリすることが可能になる。また、擬似命令を使ってプログラムの表示形式を操作し、読みやすく保守しやすくすることもできる。擬似命令のもう一つの一般的な用途は、実行時データ用の記憶領域を確保し、必要に応じてその内容を既知の値で初期化することである。
シンボリックアセンブラでは、プログラマーはメモリ位置や様々な定数に任意の名前(ラベルまたはシンボル)を関連付けることができます。通常、すべての定数と変数には名前が付けられ、命令は名前でそれらの位置を参照できるため、自己文書化コードが促進されます。実行可能コードでは、各サブルーチンの名前はそのエントリポイントに関連付けられているため、サブルーチンへの呼び出しではその名前を使用できます。サブルーチン内では、GOTO命令の宛先にラベルが付けられます。一部のアセンブラはローカルシンボルをサポートしており、これは通常シンボルとは字句的に区別されることがよくあります(例:「10$」をGOTO命令の宛先として使用する場合)。
NASMなどの一部のアセンブラは、柔軟なシンボル管理機能を備えており、プログラマはさまざまな名前空間を管理したり、データ構造内のオフセットを自動的に計算したり、リテラル値やアセンブラによる単純な計算結果を参照するラベルを割り当てたりすることができます。ラベルは、再配置可能なアドレスを持つ定数や変数を初期化するためにも使用できます。
アセンブリ言語は、他の多くのコンピュータ言語と同様に、プログラムのソースコードにコメントを追加できますが、これらのコメントはアセンブリ時に無視されます。アセンブリ言語プログラムでは、適切なコメント付けが不可欠です。なぜなら、一連のバイナリマシン命令の意味や目的を判断するのは難しい場合があるからです。コンパイラや逆アセンブラによって生成される「生の」(コメントのない)アセンブリ言語は、変更を加える必要がある場合に非常に読みづらいものとなります。
多くのアセンブラは定義済みのマクロをサポートしており、その他はプログラマ定義の(そして繰り返し再定義可能な)マクロをサポートしています。このマクロは、変数や定数が埋め込まれたテキスト行のシーケンスを含みます。マクロの定義は、最も一般的には[注5 ]アセンブラステートメント(ディレクティブ、シンボリックマシン命令、アセンブラステートメントのテンプレートなど)の組み合わせです。このテキスト行のシーケンスには、オペコードやディレクティブが含まれる場合があります。マクロが定義されると、その名前をニーモニックの代わりに使用できます。アセンブラがこのようなステートメントを処理すると、ステートメントをそのマクロに関連付けられたテキスト行に置き換え、ソースコードファイルに存在するかのように処理します(一部のアセンブラでは、置き換えテキストに存在するマクロの展開も含まれます)。この意味でのマクロは、1950年代のIBMオートコーダーにまで遡ります。 [ 32 ]
マクロアセンブラには通常、マクロの定義、変数の定義、算術式、論理式、または文字列式の結果を変数に設定する、反復処理、条件付きコード生成などを行うためのディレクティブがあります。これらのディレクティブの中には、マクロ定義内でのみ使用できるもの(例:HLASMのMEXIT)もあれば、オープンコード(マクロ定義外)内で使用できるもの(例: HLASM のAIFおよびCOPY )もあります。
アセンブリ言語における「マクロ」という用語は、C言語のプリプロセッサなど、他の言語におけるマクロよりも包括的な概念を表します。C言語では、#defineディレクティブは通常、短い1行のマクロを作成するために使用されます。アセンブラのマクロ命令は、PL/Iや他の言語のマクロと同様に、それ自体が長い「プログラム」となり、アセンブリ中にアセンブラによって解釈されて実行されます。
マクロは「短い」名前を持つことができ、展開すると数行、あるいは多数のコード行に及ぶため、アセンブリ言語プログラムをはるかに短く見せることができ、高水準言語と同様にソースコードの行数を減らすことができます。また、アセンブリプログラムに高レベルの構造を追加したり、パラメータを介して組み込みデバッグコードを導入したり、その他同様の機能を利用することもできます。
マクロアセンブラでは、マクロがパラメータを受け取ることができる場合が多い。一部のアセンブラには、オプションパラメータ、シンボル変数、条件分岐、文字列操作、算術演算といった高水準言語要素を組み込んだ、非常に高度なマクロ言語が含まれている。これらの要素はすべて、マクロの実行中に使用可能であり、マクロがコンテキストを保存したり、情報を交換したりできる。そのため、マクロはマクロ引数に基づいて、多数のアセンブリ言語命令やデータ定義を生成できる。これは、例えばレコード形式のデータ構造や「展開された」ループを生成したり、複雑なパラメータに基づいてアルゴリズム全体を生成したりするために使用できる。例えば、「ソート」マクロは、複雑なソートキーの指定を受け取り、その特定のキー用に作成されたコードを生成できる。この場合、指定を解釈する一般的な手順に必要な実行時テストは不要となる。このようなマクロスイートを使用して大幅に拡張されたアセンブリ言語を使用している組織は、プログラマがコンピュータの最も低レベルの概念要素を扱っていないため、より高水準の言語で作業していると考えることができる。この点を強調するために、SNOBOL4 (1967)では、仮想マシン用のアセンブリ言語である SNOBOL 実装言語 (SIL) で書かれた初期の仮想マシンを実装するためにマクロが使用されました。ターゲットマシンは、マクロアセンブラを使用してこれをネイティブコードに変換します。[ 33 ]これにより、当時としては高い移植性が実現しました。
マクロは、メインフレーム時代には、特定の顧客向けに大規模なソフトウェアシステムをカスタマイズするために使用され、また、顧客側の担当者が、製造元のオペレーティングシステムの特定のバージョンを作成することで、雇用主のニーズを満たすためにも使用されていました。これは、例えば、IBMのConversational Monitor System / Virtual Machine ( VM/CMS )や、IBMの「リアルタイムトランザクション処理」アドオンであるCustomer Information Control System CICS、そして1970年代に始まり、現在でも多くの大規模なコンピュータ予約システム(CRS)やクレジットカードシステムで稼働している航空会社/金融システムであるACP / TPFを扱うシステムプログラマによって行われていました。
アセンブラのマクロ処理機能のみを使用して、まったく異なる言語で記述されたコードを生成することも可能です。たとえば、アセンブラに任意のコードを生成するように指示するアセンブリ時演算子内に COBOL コードの行を含む純粋なマクロアセンブラプログラムを使用して、COBOLで記述されたプログラムのバージョンを生成することができます。IBM OS/360はマクロを使用してシステム生成を実行します。ユーザーは、一連のアセンブラマクロをコーディングすることでオプションを指定します。これらのマクロをアセンブルすると、ジョブ制御言語やユーティリティ制御ステートメントを含む、システムを構築するためのジョブストリームが生成されます。
これは、1960年代に認識されたように、「マクロ処理」の概念が「アセンブリ」の概念とは独立しているためであり、前者は現代の用語で言えば、オブジェクトコードを生成するというよりは、ワードプロセッシングやテキスト処理に近い。マクロ処理の概念は、変数を設定したり、その値に対して条件付きテストを実行したりするための「プリプロセッサ命令」をサポートするC言語に現れ、現在も存在している。アセンブラ内の以前のマクロプロセッサとは異なり、Cプリプロセッサは、ループや「ジャンプ」の機能がないため、チューリング完全ではない。後者の機能により、プログラムはループすることができる。
マクロ処理は強力な機能であるにもかかわらず、多くの高級言語では使われなくなってしまった(C、C++、PL/Iは大きな例外である)一方で、アセンブラでは依然として広く利用されている。
マクロパラメータの置換は厳密に名前で行われます。マクロ処理時には、パラメータの値がテキストとしてその名前に置き換えられます。最も有名なバグの種類は、マクロ作成者が名前を期待していたにもかかわらず、パラメータ自体が単純な名前ではなく式であった場合に発生するものでした。マクロでは、次のようになります。
foo: マクロ a a*bをロードする
意図としては、呼び出し元が変数名を指定し、「グローバル」変数または定数 b が「a」に乗算されるというものであった。foo がパラメータ で呼び出されるとa-c、 のマクロ展開がload a-c*b行われる。曖昧さを避けるため、マクロプロセッサのユーザーはマクロ定義内で仮パラメータを括弧で囲むことができ、呼び出し元は入力パラメータを括弧で囲むことができる。[ 34 ]
マクロのパッケージが作成され、実行フローをエンコードするための構造化プログラミング要素が提供されています。このアプローチの最も初期の例は、ハーラン・ミルズが最初に提案し(1970 年 3 月)、IBM の連邦システム部門のマービン・ケスラーが実装した Concept-14 マクロ セット[ 35 ]です。これは OS/360 アセンブラ プログラム用の IF/ELSE/ENDIF および同様の制御フロー ブロックを提供しました。これは、アセンブリ言語でスパゲッティ コードを引き起こす主な要因の 1 つは、アセンブリ コードでのGOTO操作の使用を減らすか排除する方法でした。このアプローチは 1980 年代初頭(大規模なアセンブリ言語の使用の末期)に広く受け入れられました。IBM の High Level Assembler Toolkit [ 36 ]には、このようなマクロ パッケージが含まれています。
もう1つの設計はA-Natural [ 37 ]で、 Whitesmiths Ltd.(UnixライクなIdrisオペレーティングシステムの開発元であり、最初の商用Cコンパイラとされるものの開発元)による8080/ Z80プロセッサ向けの「ストリーム指向」アセンブラでした。この言語は、オペコード、レジスタ、メモリ参照などの生のマシン要素を扱うためアセンブラに分類されましたが、実行順序を示す式構文が組み込まれていました。括弧やその他の特殊記号とブロック指向の構造化プログラミング構造によって、生成される命令の順序が制御されました。A-naturalは、手書きコーディング用ではなく、Cコンパイラのオブジェクト言語として構築されましたが、その論理構文は一部のファンを獲得しました。
大規模アセンブリ言語開発の衰退以来、より高度なアセンブラに対する明らかな需要はほとんどない。[ 38 ]それにもかかわらず、リソースの制約や対象システムのアーキテクチャの特殊性により高水準言語の効果的な使用が妨げられる場合には、アセンブラは依然として開発され、適用されている。[ 39 ]
強力なマクロエンジンを備えたアセンブラでは、マクロを介した構造化プログラミングが可能になります。例えば、Masm32パッケージに付属するswitchマクロなどが挙げられます(このコードは完全なプログラムです)。
include \ masm32 \ include \ masm32rt.inc ; Masm32ライブラリを使用する.code demomain: REPEAT 20 switch rv ( nrandom , 9 ) ; 0 から 8 の間の数値を生成mov ecx , 7 case 0 print "case 0" case ecx ; 他のほとんどのプログラミング言語とは異なり、print "case 7" ; Masm32 の switch では「可変ケース」が使用可能case 1 .. 3 .if eax == 1 print "case 1" .elseif eax == 2 print "case 2" .else print "cases 1 to 3: other" .endif case 4 , 6 , 8 print "cases 4, 6 or 8" default mov ebx , 19 ; 20 個の星を出力.Repeat print "*" dec ebx .Until Sign? ;符号フラグが設定されるまでループします。endsw print chr$ ( 13 , 10 ) ENDM exit end demomain内蔵プログラム方式のコンピュータが導入された当時、プログラムは機械語で記述され、パンチ紙テープからコンピュータにロードされるか、コンソールスイッチから直接メモリに切り替えられました。キャスリーン・ブースは、1947年にロンドン大学バークベック校のARC2で作業中に始めた理論的研究に基づいて「アセンブリ言語の発明者」とされています[ 40 ] [ 41 ] 。これは、アンドリュー・ブース(後に彼女の夫となる)が、高等研究所の数学者ジョン・フォン・ノイマンと物理学者ハーマン・ゴールドスタインに相談した後に始まりました[ 41 ] [ 42 ]。
1948 年後半、電子遅延記憶自動計算機(EDSAC) のブートストラッププログラムにアセンブラ (「初期命令」と呼ばれる) が組み込まれました。これは、 IEEE コンピュータ ソサエティによって最初の「アセンブラ」の作成者として認められているDavid Wheelerによって開発された 1 文字のニーモニックを使用しています。 [ 22 ] [ 43 ] [ 44 ] EDSAC に関するレポートでは、フィールドを命令語に結合するプロセスに「アセンブリ」という用語が導入されました。[ 45 ] SOAP ( Symbolic Optimal Assembly Program ) は、1955 年に Stan Poley によって書かれたIBM 650コンピュータ用のアセンブリ言語です。[ 46 ]
アセンブリ言語は、初期のコンピュータで必要だった、エラーが発生しやすく、面倒で時間のかかる第一世代のプログラミングの多くを排除し、プログラマを数値コードの記憶やアドレスの計算といった面倒な作業から解放しました。かつてはあらゆる種類のプログラミングに広く使用されていました。1950年代後半には、プログラミングの生産性向上を目指して、アセンブリ言語の使用は高水準言語にほぼ取って代わられました。[ 47 ]今日でも、アセンブリ言語はハードウェアの直接操作、特殊なプロセッサ命令へのアクセス、または重大なパフォーマンスの問題に対処するために使用されています。[ 48 ]典型的な用途としては、デバイスドライバ、低水準の組み込みシステム、リアルタイムシステムなどがあります(§ 現在の使用法を参照)。
数多くのプログラムが完全にアセンブリ言語で記述されていました。Burroughs MCP(1961年)は、オペレーティングシステムが完全にアセンブリ言語で開発されなかった最初のコンピュータであり、 Algolの方言であるExecutive Systems Problem Oriented Language (ESPOL)で記述されていました。多くの商用アプリケーションもアセンブリ言語で記述されており、大企業が開発したIBMメインフレームソフトウェアの大部分もアセンブリ言語で書かれていました。COBOL、FORTRAN、および一部のPL/Iが最終的にアセンブリ言語に取って代わりましたが、多くの大企業は1990年代までアセンブリ言語のアプリケーションインフラストラクチャを維持していました。
アセンブリ言語は、 Apple II、Atari 8ビットコンピュータ、ZX Spectrum、Commodore 64などの8ビットホームコンピュータの主要な開発言語でした。これらのシステム上のインタプリタBASICでは、最大の実行速度や利用可能なハードウェアを最大限に活用するための機能の完全な使用は提供されませんでした。アセンブリ言語は、Atari 2600やNintendo Entertainment Systemなどの8ビットコンソールのプログラミングのデフォルトの選択肢でした。[ 49 ]
MS-DOS、Turbo Pascal、Lotus 1-2-3表計算ソフトなど、IBM PC互換機の主要ソフトウェアはアセンブリ言語で書かれていました。コンピュータの速度が飛躍的に向上するにつれて、アセンブリ言語は、Doomのレンダリングなど、プログラムの一部を高速化するためのツールとなり、主要な開発言語ではなくなりました。1990年代には、アセンブリ言語はセガサターンなどのシステムのパフォーマンスを最大化するために使用され[ 50 ]、Mortal KombatやNBA JamなどのTMS34010統合CPU/GPUを使用するアーケードハードウェアの主要言語として使用されました。
アセンブリ言語の有用性や性能については、高水準言語と比較して議論がなされてきた。[ 51 ]
アセンブリ言語は特定のニッチな用途で重要視されるが(下記参照)、最適化のための他のツールも存在する。[ 52 ]
2017年7月現在 プログラミング言語の人気度を示すTIOBE インデックスでは、アセンブリ言語は、たとえばVisual Basicより上位の 11 位にランクされています。 [ 53 ]アセンブラは、速度最適化またはサイズ最適化に使用できます。速度最適化の場合、最新の最適化コンパイラは、いくつかの反例があるにもかかわらず、高水準言語を手書きのアセンブリと同じくらい高速に実行できるコードに変換するとされています。 [ 54 ] [ 55 ] [ 56 ] [ 57 ]最新のプロセッサとメモリサブシステムの複雑さにより、コンパイラとアセンブリプログラマの両方にとって効果的な最適化がますます困難になっています。[ 58 ] [ 59 ]プロセッサのパフォーマンスの向上により、ほとんどの CPU はほとんどの時間アイドル状態になり、キャッシュ ミス、 I /O操作、ページングなどの予測可能なボトルネックによって遅延が発生するため、生のコード実行速度は多くのプログラマにとって問題ではなくなりました。[60]
アセンブリ言語によるプログラミングがより一般的に用いられているコンピュータプログラミングの分野は、依然としていくつか存在する。
アセンブリ言語は、ほとんどのコンピュータ科学および電子工学プログラムで今でも教えられています。今日ではアセンブリ言語をツールとして日常的に使用するプログラマーは少ないものの、その根底にある概念は依然として重要です。バイナリ演算、メモリ割り当て、スタック処理、文字セットエンコーディング、割り込み処理、コンパイラ設計といった基本的なトピックは、コンピュータがハードウェアレベルでどのように動作するかを理解せずに詳細に研究することは困難です。コンピュータの動作は基本的に命令セットによって定義されるため、これらの概念を学ぶ論理的な方法はアセンブリ言語を研究することです。ほとんどの現代のコンピュータは同様の命令セットを持っています。したがって、単一のアセンブリ言語を研究するだけで、基本的な概念を学び、アセンブリ言語の使用が適切な状況を認識し、高水準言語から効率的な実行可能コードを作成する方法を理解するのに十分です。[ 25 ]
記号機械語とも呼ばれる。
機械語でのプログラミングと同じ利点がありますが、より簡単です。
{{cite book}}ISBN /日付の不一致(ヘルプ)コンパイル型の低レベルコンピュータ言語です。基本的にアセンブラのニーモニックを特定のCPUが理解できるコマンドに1対1で直接変換するため、プロセッサに依存します。これらのアセンブラのニーモニックは、そのプロセッサの命令セットです。
アセンブリ言語は特定のコンピュータアーキテクチャに特化していることが多いため、アセンブリ言語には複数の種類があります。ARM はますます人気が高まっているアセンブリ言語です。
メタ アセンブラとして使用され、ユーザーが独自のプログラミング言語を設計し、最小限の労力でそのような言語のプロセッサを生成することを可能にします。
マクロ命令をコーディングする際の 1401 オートコーダーの使用に関して、以下の軽微な制限または制約が適用されます...
以下のテキストに含まれる非独創的なアイデアは、多くの情報源から得られたものです。...しかし、多くの実りある議論をしてくださったジョン フォン ノイマン教授とヘルマン ゴールドスタイン博士に感謝の意を表すべきだと感じています...
現代のプログラミングの世界におけるアセンブリ言語の適用可能性については、常に議論が続いています。
... 設計変更はパフォーマンスに大きな影響を与える傾向があるため、... アセンブリ言語に直接移行すべきではない。
{{cite web}}: CS1メンテナンス: アーカイブサービスは非推奨になりました (リンク)