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