アセンブリ言語

出典: フリー百科事典『ウィキペディア(Wikipedia)』

これはこのページの過去の版です。野良人 (会話 | 投稿記録) による 2022年11月2日 (水) 12:38個人設定で未設定ならUTC)時点の版 (→‎関連項目: 不要な別名表記を解除)であり、現在の版とは大きく異なる場合があります。

モトローラ MC6800 のアセンブリ言語のソースコード

アセンブリ言語(アセンブリげんご、: assembly language)とは、プログラミング言語の一つ。一般的にはアセンブラと呼ばれ、低水準言語に分類される[1]

CPU機械語と一対一で対応するが、人間が理解しやすいよう、ニーモニックという単語で表現したものである。 そういう意味でアセンブリ言語とは、機械語そのものと言える。

用語

プログラムを組み立てる際、アセンブリ言語登場以前は機械語を使っていたが、組み立て機(assembler)の登場により、機械語より簡略な表現(mnemonic)からプログラムを組み立てる(assemble)ことが出来るようになり当時としては作業を効率化できるようになった。

このためこの工程をカタカナ表記し、組み立て機をアセンブラ(assembler)、組み立てをアセンブル(assemble)、組み立てに使う簡略表現(mnemonic)の言語体系を組み立て用の言語という意味でアセンブリ(assembly)と呼ぶ。

なお不正確な用法だが、アセンブリ言語の意味で「アセンブラ」または「アセンブラ言語」 (Assembler Language) と呼ぶ場合がある[注 1][2]。また、初期のコンピュータでは、「アセンブラ」を「アセンブリプログラム」と呼ぶ例もあった[3]

概要

プロセッサが直接実行できる言語は、機械語である。しかし機械語は2進数の羅列なので、人間には極めて理解しにくい。そこで機械語を直接書くのではなく、ニーモニックと呼ぶ命令語でプログラムを記述することで、人間により分かりやすくしたものがアセンブリ言語である。ただしアセンブリ言語の意味は 後述のように個々のプロセッサに依存し、プロセッサを介したハードウェア制御も含むため多くの場合は同一プロセッサを用いていてもソフトウェアの互換性は限定的である。

アセンブリ言語の命令には、機械語に対応したものと、アセンブラに対する命令(疑似命令)やマクロがある[注 2]。これらが機械語と1対1で対応するため、プログラマがCPUの動作を把握しながらプログラムを記述することができる。

アセンブリ言語では、機械語に相当する低水準な操作またはオペコードニーモニックで表す。オペコードによっては、機械語命令の一部として1つまたは複数のオペランドが必要である。また、多くのアセンブリ言語は、オペランドとしてラベルやシンボルを使ってアドレスや定数を表すことができ、それらの値をプログラム内にそのまま書くこと(ハードコーディング)を防ぐことができる。マクロアセンブラマクロ命令機能を備えており、アセンブリ言語のテキストに名前を事前に割り当て、その名前を使うことで他のコードにそのテキストを挿入できる。多くのアセンブラは、プログラム開発を支援したり、アセンブリ過程を制御したり、デバッグを支援したりといった付加機構を備えている。

歴史

機械語は、実行したい計算の内容をCPUの内部構造に依存した非常に単純な操作に分割・変換したものであるため、人間には理解しづらい。したがって、機械語を並べながらプログラミングするのは、人間のプログラマにとっては負担が大きかった。

そこで、機械語そのものを書く代わりに、機械語の「意味」に相当する短い記号や単語を対応させ、それを記述してプログラミングをすることが考えられた。

世界で最初に実用的に稼働したノイマン型電子計算機とされるEDSACのローダ(外部記憶装置からプログラムやデータを読み出して主記憶装置に書き込むプログラム)でも、原始的なアセンブラの機能がすでに実装されている。EDSACは、ワード指向アーキテクチャで命令長が1ワードの固定長命令のマシンであり、入力機器は紙テープでありキャラクタ指向である。紙テープ上のA100Fという文字列から十進法を数値に変換するなどの処理をおこない、「100番地の値をアキュムレータに加算する」という1ワードの命令を生成するといった機能が、EDSACのローダには実装されていた。

機械語への変換は、人間が手で行うこともある。これをハンド・アセンブルと呼ぶ。単に定められた規則に従って記号や単語から機械語を生成するだけなので、自動的に機械語を出力するプログラムが作られるようになった。このプログラムをアセンブラという。

コンピュータの歴史の初期には、このような、プログラムによって機械語プログラムを生成することを自動プログラミングと呼んだ。

ドナルド・ギリースは、まだ発明されていなかったアセンブラを開発中に、フォン・ノイマンから開発を即座に止めるように言われた、という1950年代初期ならではの逸話がある。当時は、人間が手作業でもできるような瑣末な仕事をコンピュータにさせるような時代が来るとは考えられておらず、単に時間の無駄だとノイマンは考えたのである。

主要概念

アセンブラ

アセンブラは、アセンブリ命令ニーモニックをオペコードに変換し、シンボル名をメモリ位置や他の実体に変換することでオブジェクトコードを生成する[4]。シンボル名による参照の利用はアセンブラの重要な機能であり、面倒な計算やプログラム修正に伴うアドレスの更新の手間を省くことができる。多くのアセンブラはマクロ機能を備えており、よく使用する命令列をサブルーチン呼び出しではなくインライン展開することができる[5]。また、オブジェクトコードを生成する際、ローダ用情報も併せて生成するアセンブラもある[6]

パス回数

実行ファイルを生成するのにソースファイルを何回パス(走査)するかでアセンブラを分類できる。

ワンパスアセンブラ
ソースコードを1回だけパスするアセンブラ。定義される前にシンボルが使われているとオブジェクトコードの最後に "errata" を置く必要があり、リンカまたはローダが未定義シンボルが使われていた位置にあるプレースホルダーを書き換える。あるいは、未定義なシンボルを使用するとエラーになる。
マルチパスアセンブラ
最初のパスで全シンボルとその値の表を作成し、その表を使ってその後のパスでコードを生成する。

どちらの場合も、アセンブラは最初のパスで各命令のサイズを確定させる必要があり、それによって後に出現するシンボルのアドレスを計算する。命令のサイズは後から定義されるオペランドの型や距離に依存することがあるため、アセンブラは最初のパスでは悲観的な見積もりをし、必要に応じてその後のパスまたは errata にて1つ以上のNOP命令(何もしない命令)を挿入してすき間を埋める必要がある。最適化を行うアセンブラでは、最初の悲観的コードをその後のパスで稠密なコードに書き換えてアドレスの再計算を行うことがある。

もともとワンパスアセンブラは高速であるためよく使われていた。マルチパス動作をするには、磁気テープを巻き戻したりパンチカードのデッキをセットし直して読み込む必要があったためである。現代のコンピュータではマルチパスであってもそのような遅延は生じない。マルチパスアセンブラは errata がないため、リンク処理(アセンブラが直接実行コードを生成する場合はローダの処理)が高速化される[7]

高水準アセンブラ

より洗練された高水準アセンブラ英語版は言語として次のような抽象化を提供する。

クロスアセンブラとメタアセンブラ

クロスアセンブラクロスコンパイラと似ており、あるプラットフォームで動作するが、そのプラットフォームではなく別のプラットフォーム向けのオブジェクトコードを生成するアセンブラを指す。小型の組み込みシステムなど、対象システムでアセンブラを動作させられない場合に使用することが多い。その場合、生成した機械語コードを対象システムに転送する何らかの手段が必要である。単に機械語コードをバイト単位に転送することもできるが、ASCII表現の転送用フォーマットがメーカーから提供されている(モトローラのSREC、インテルのHEXなど)。

メタアセンブラは、アセンブリ言語の文法や意味論を記述したものを入力とし、その言語のためのアセンブラを出力するプログラムである[10]

アセンブリ言語

アセンブリ言語で書かれたプログラムは、プロセッサの命令に対応したニーモニックの列、ディレクティブや擬似命令と呼ばれるメタな文、コメント、データで構成されている。ニーモニックとは、プロセッサが機械語の命令に応じて実行する動作を表現する英単語またはその省略形(例えばLoadまたはその省略形LDなど)で表現したものである。通常の文はオペコードのニーモニックで始まり、パラメータ(データ、引数)のリストがそれに続く[11]

なかには、パイプライン処理などを最適化するために命令順序を入れ替えたり、ラベルの位置関係によってアドレッシングモードを最適化するアセンブラもあり、必ずしもソーステキストの記述とアセンブルの結果が直接対応するとは限らない。

オペランドにはソースとデスティネーションの二種類があり、データとして読み取られるのがソースで、オペコードで示された命令の実行結果が格納されるのがデスティネーションである。ソースには定数・レジスタ・メモリのいずれか、デスティネーションにはレジスタ・メモリのいずれかを指定する。

なお、オペランドの記述順序は、上の例では「デスティネーション, ソース」だが、「ソース, デスティネーション」の順に書くアセンブリ言語もある。また、オペランドの個数は命令によって異なり、典型的なオペランドは0以上のソースと1つのデスティネーションからなるが、プロセッサにより、暗黙のソースやデスティネーションを持つためにそれらを記述しない命令や、オペランドを持たない命令を持つ場合もある。

例えば、次の命令はx86/IA-32プロセッサのもので、8ビット即値レジスタに入れる命令である。この命令のバイナリコードは 10110 で、その後に3ビットのレジスタを指定する識別子が続く。AL レジスタの識別子は 000 なので、次に示す機械語AL レジスタに 01100001 というデータをロードする[8]

10110000 01100001

このバイナリコードを人間が読みやすいように十六進法で表現すると次のようになる。

B0 61

ここで、B0は「ALに後続の値をコピーする」ことを意味し、61は01100001を十六進法で表したもの(十進法では97)である。インテルのアセンブリ言語では、この種の命令に MOV というニーモニックを割り当てており、セミコロン以下に説明的コメントを添えたアセンブリ言語での表現は次のようになる。

MOV AL, 61h       ; Load AL with 97 decimal (61 hex)

この場合、定数61Hがソース、レジスタALがデスティネーションに該当し、命令が実行されると、定数61Hが、レジスタALに単純に格納される。これが人間にとってはさらに読みやすく覚えやすい。

ニーモニックとオペコードの対応は、命令セットアーキテクチャ、あるいは個々のアセンブラの実装により異なることもある。違うオペコードの命令でも同一のニーモニックの場合もあれば、同じオペコードの命令でもオペランド違いなどで違うニーモニックを当てている場合もある。例えば、Intelのi8080と、ZilogのZ80では一部の挙動の違いはあれど、共通する機能のバイナリはほぼ同一だが、割り当てられているニーモニックの分類や表記はまるで違うものとなっている。

前述のインテルの MOV のようにデータの転送の多くを同一の命令あるいはニーモニックとする場合もあれば、データのコピー/移動の方向などによって別々の命令あるいはニーモニックとする場合もある(「メモリからレジスタへの移動」を L、「レジスタからメモリへの移動」を ST、「レジスタからレジスタへの移動」を LR、「即値をメモリへ移動」を MVI など)。(この段落では命令セットの設計の話とアセンブリ言語の話を一緒にしている)

インテルのオペコード 10110000(B0)は8ビットの値を AL レジスタにコピーするが、10110001(B1)はCLレジスタにコピーし、10110010(B2)は DL レジスタにコピーする。これらをアセンブリ言語で表現すると次のようになる[8]

MOV AL, 1h        ; Load AL with immediate value 1
MOV CL, 2h        ; Load CL with immediate value 2
MOV DL, 3h        ; Load DL with immediate value 3

MOVの構文には次の例のようにさらに複雑なものもある[12]

MOV EAX, [EBX]	  ; Move the 4 bytes in memory at the address contained in EBX into EAX
MOV [ESI+EAX], CL ; Move the contents of CL into the byte at address ESI+EAX

MOVというニーモニックを使った文は、その内容によってアセンブラが88-8E、A0-A3、B0-B8、C6、C7のいずれかのオペコードに変換するので、プログラマはオペコードを知る必要がないし、オペコードを覚える必要もない[8]

アセンブリ言語から機械語への変換はアセンブラが行うが、逆方向の変換の一部は逆アセンブラが行う。高水準言語とは異なり、単純なアセンブリ構文と機械語命令には1対1の関係がある。しかしアセンブラは擬似命令(マクロの一種)を提供していることがあり、1つの擬似命令をよく使われる機械語命令列に展開する。例えば、大小比較による条件分岐命令を持たないマシンの場合、アセンブラで大小比較命令とゼロなら分岐する条件分岐命令(比較命令の結果を使用)に展開される擬似命令を用意したりする。多くの高機能アセンブラは豊富なマクロ言語機能を備えており、複雑なコード列やデータシーケンスを生成できる。

コンピュータ・アーキテクチャはそれぞれ独自の機械語を持つ。アーキテクチャが異なれば、サポートする命令の数や種類が異なり、レジスタの大きさや個数が異なり、データの格納形式が異なる。汎用コンピュータの多くは機能的にほぼ同等だが、実装は異なるので、アセンブリ言語にはその差異が反映されている。

機械語の命令セットは、プロセッサによって異なる。命令セットが同じでも様々なニーモニックやアセンブリ言語の文法が存在しうる。一般にプロセッサの設計者あるいは製造者によって定義されたニーモニックが標準とされるが、アセンブラによってはこれと異なるニーモニックを用いる場合もある(GNUアセンブラのgasのインテルプロセッサ用など)。

言語設計

基本要素

アセンブラの開発者によって用語の使い方に大きな差異があり、文の分類などが異なる。例えば、マシンのニーモニックや拡張ニーモニック以外は全て擬似命令と呼ぶ場合もある。典型的なアセンブリ言語は、プログラムの操作の定義に使われる命令文を以下の3種類に分類する。

  • オペコードのニーモニック
  • データセクション
  • アセンブリディレクティブ

オペコードのニーモニックと拡張ニーモニック

アセンブリ言語の命令文は一般に非常に単純で、高水準言語のそれとは異なる。一般にニーモニックは1つの機械語命令(オペコード)のシンボル名であり、個々の機械語命令には少なくとも1つのオペコード・ニーモニックが対応している。命令は一般に「オペコード」と0以上の「オペランド」で構成される。多くの命令は1つまたは2つの値を参照する。オペランドには即値(命令内に置かれる値)、レジスタ(暗黙のうちに使用される場合もある)、記憶装置内のデータの位置を示すアドレスなどがある。「拡張ニーモニック」はオペコードと特定オペランドの組合せを表すのに使われることが多い。例えば、System/360では、BC命令にマスク15を組み合わせたものがB、BC命令にマスク0を組み合わせたものがNOPという拡張ニーモニックで表される。

「拡張ニーモニック」は命令の特殊な用途をサポートするのに使われることが多く、本来の命令の名称からはその用途が連想できないときに使うことが多い。例えば、多くのCPUは明示的にNOP命令を用意していないが、その用途に使える命令は存在する。8086ではxchg ax,axという命令がnopとして使えるので、アセンブリ言語でnopを記述すると xchg ax,ax という命令に変換される。逆アセンブラにもこのあたりを認識し、xchg ax,axnopに変換するものがある。同様にIBMのSystem/360System/370のアセンブラでは、拡張ニーモニックNOPNOPRを使用し、それぞれBCBCRのマスク0の命令に変換する。SPARCアーキテクチャでは、拡張ニーモニックをsynthetic instructionsと呼んでいる[13]

アセンブラによっては単純な組み込みマクロ命令を備えており、数個の機械語命令に展開される。例えば、一部のZ80用アセンブラでは、ld hl,bc というマクロ命令を ld l,cld h,b という2命令に展開する[14]

データセクション

データと変数を保持するデータ要素を定義するのに使われる命令文がある。データの型、長さ、境界(アライメント)を定義する。また、そのデータがプログラム外部(別ファイルでアセンブルされたプログラム)からも利用可能なのか、それともデータセクションを定義したプログラム内でのみ使用可能なのかも定義できる。一部のアセンブラはこれを擬似命令に分類している。

アセンブリディレクティブ

アセンブリディレクティブは、擬似命令とも呼ばれ、アセンブラがアセンブリ実施中に実行すべき命令となっている[4]。プログラマが入力するパラメータによって、異なった形でアセンブルが行われるよう指示することができる。また、プログラムの見た目を操作して、可読性と保守性を向上させるのにも使われる。例えば、記憶装置の領域を予約し、その初期内容を指定するディレクティブなどがある。ディレクティブの名称はドットで始まることが多く、それによって通常のニーモニックと区別している。

擬似オペコード(pseudo-opcode)と言った場合、オブジェクトコードを実際に生成するディレクティブのみを指すこともある[15]

ラベル/シンボル

シンボリックアセンブラでは、任意の名前(ラベルまたはシンボル)とメモリ位置を対応付けることができる。通常、定数や変数に名前をつけることができ、命令文ではそれらの位置を名前で参照できる。実行コードではサブルーチンのエントリポイントと名前を関連付け、サブルーチンを名前で呼び出すことができる。サブルーチン内では、分岐命令の分岐先をラベルで示すことができる。一部のアセンブラは「ローカルシンボル」をサポートしており、通常のシンボルとは語彙的に区別する(例えば、"10$"を分岐先に使用する、など)。

一部のアセンブラは柔軟なシンボル管理を提供しており、複数の名前空間を管理したり、データ構造内のオフセットを自動的に計算したり、リテラル値やアセンブラが実施した単純な計算結果を参照するラベルを割り当てたりすることができる。ラベルは定数や変数をリロケータブルなアドレスで初期化するのにも使える。

コメント

他の言語と同様アセンブリ言語でもソースコードにコメントを付与でき、アセンブラはそれらを無視できる。高水準言語に比べて可読性が低いため、コメントの重要性は高い。

これらの機能を多用することで低水準のコードのコーディングや保守の問題を大幅に単純化することができる。コンパイラや逆アセンブラが生成するアセンブリソースはコメントがなく、自動生成されるシンボルが意味不明で、データ定義もないため、非常に可読性が低い。

マクロ

マクロの使用はアセンブリ言語に限らず(さらには、プログラミングに限らず)一般的な技法だが、アセンブリ言語でのプログラミングでは特に有用である。m4のような汎用のマクロプロセッサが使われることもあれば、アセンブラに組み込まれたマクロもある。以下ではアセンブラに組み込まれたマクロについて述べる。

多くのアセンブラは事前定義されたマクロをサポートしており、中にはユーザーが定義可能なマクロをサポートしているものもある。マクロは命令列と変数や定数の定義を埋め込むことができ、その中にオペコードやディレクティブの列が含まれる。マクロが定義されると、その名前をニーモニックとして使用できる。アセンブラはソース内にマクロを発見すると、対応するコード列に置き換え、元からソースにそのコード列が存在したかのように処理をする(アセンブラによっては、マクロを展開した中にマクロがあれば、それも展開することができる)。

マクロは短い名前だが、数行からかなり長い行数のコードに展開されるので、アセンブリ言語のプログラムの見た目を短くすることができる。また、マクロによってアセンブリ言語プログラムに高水準の構造を追加でき、パラメータ付きのデバッグ用コードを埋め込むなどの利用法がある。

引数が使えるマクロを持つものもある。中には洗練されたマクロ言語を備えたアセンブラもあり、オプションパラメータ、記号変数、条件文、文字列操作、算術演算などの高水準言語要素を持ち、マクロ実行中にそれらを使用でき、またマクロがコンテキストをセーブしたり情報を交換したりすることも可能である。そういったアセンブラでは、マクロの引数に基づいて大量のアセンブリ言語命令またはデータ定義を生成できる。例えば、レコード風のデータ構造や展開されたループを生成するのに使え、複雑なパラメータに基づいてアルゴリズム全体を生成することもできる。そのようなマクロで大いに拡張されたアセンブリ言語を使用すると、あたかも一種の高水準言語であるかのようにプログラムを書くことができることもある。

メインフレームの時代には、マクロは特定顧客の大規模ソフトウェアシステムのカスタマイズや、メーカーのオペレーティングシステムを顧客の要望に合わせた特注版にするのに使われていた。例えば、IBMVM/CMS、リアルタイムトランザクション処理用アドオン、CICSACP英語版/TPFコンピュータ予約システム (CRS) やクレジットカード会社で使われているトランザクションOS)などで使われてきた。

一方で、(やはりアセンブリ言語に限られないが)マクロには「落とし穴」も多い。最も単純なものとしては、式としての意味に従って変換されるのではなく、文字列から対応する文字列に変換されるためによるバグがある。例えば、次のようなマクロがあるとする。

foo: macro a
load a*b

このマクロは foo という名前で仮引数 a をとる(関数形式マクロ)。呼出・展開時にaに渡された実引数と、(グローバル変数または定数である)bとを掛け、loadする。しかし実引数が、a-cというような式であった場合、マクロとしては、load a-c*bと展開され、(おそらく)意図されていない結果を招く。これを防ぐにはマクロ定義内で仮引数は常に括弧で囲むようにするか、呼び出し時に実引数が問題を起こしそうな式の場合は括弧で囲むようにする[16]

このような問題は括弧の記述で解決することが多いが、マクロにおいて発生する問題として、一般に容易に解決できないことが知られている「名前の捕捉」という問題がある。たとえば直前の例のマクロの場合、実引数がbという名前であったとするとload b*bとなって、実引数の自乗を計算する、という、(おそらく)前述の例よりももっと意図されていない結果を招く。このようなマクロの性質を一般にこの分野の専門用語で「不健全」と言う。詳細は「健全なマクロ」の記事を参照のこと。

構造化プログラミングサポート

構造化プログラミングの要素を取り入れたアセンブラもある。最初期の例として"Concept-14 macro set"がある。Dr. H.D. Mills(March, 1970)が提案し、IBMの連邦政府システム部門のMarvin Kesslerが実装したもので、System/360のマクロアセンブラにIF/ELSE/ENDIFなどの制御構造を導入した[17]。これはRatforなどと同様の発想で、コードの表面上からGOTOを消し、制御構造風な読みやすいマクロでコードを書けるというものだった。1980年代にはこの手法が広まった。

独特の設計として、Whitesmiths Ltd.Unix系OS IdrisCコンパイラで有名)が開発した8080/Z80プロセッサ用「ストリーム指向」アセンブラ A-natural がある。この言語はオペコードやレジスタ、メモリ参照といった要素をそのまま扱えるためアセンブラと呼ばれているが、実行順序を示す構文を取り入れていた。括弧やその他の記号でプログラムのブロック構造を指定したり、生成した命令列の実行順序を制御できる。A-naturalは同社のCコンパイラが中間コードとしており、人間が直接使うものではなかったが、その構文にはファンも存在した。

また構造化プログラミングとは若干異なるが、キャリーラボBASIC風の文法のアセンブリ言語 BASE を開発した。Z80用のBASE-80とMC6809用のBASE-09がある。BASEの表記例は下記の通り(BASE-09)。

S[A,B,X,U
A=$80
A=A+$C0
S]A,B,X,U,PC

上記の記述は下記のアセンブラ表記に対応する。

PSHS A,B,X,U
LDA #$80
ADDA #$C0
PULS A,B,X,U,PC

その後、アセンブリ言語で大規模システムを開発することが少なくなり、アセンブリ言語の高機能化の需要が減っていった[18]。それでも、資源の制約が強い場合やターゲットシステムのアーキテクチャが高水準言語の効率的使用を妨げている場合、アセンブラは有用であり、高機能なアセンブラの開発も行われている[19]

利用

歴史的観点

アセンブリ言語は、ごく単純なものまで含めれば、プログラム内蔵方式のコンピュータの最初期の1940年代から存在している。EDSAC (1949) の initial orders(現代の用語ではブートローダーに相当するもの)は、テープにパンチされた十進によるアドレスを、内部表現の二進に変換するなどの機能を持っていた(命令については、「1文字のニーモニック」に見えるかもしれないが、それは実際には同機の機械語そのものである)[20]ナサニエル・ロチェスターは1954年に IBM 701 用アセンブラを書いている。1955年、Stan Poley が IBM 650 用言語アセンブリSOAP (Symbolic Optimal Assembly Program) を開発した[21]

アセンブリ言語は、初期のコンピュータでプログラミングの入力ミス削減や時間短縮に貢献し、機械語コード参照やアドレス計算といった退屈な作業からプログラマを解放した。その後高水準言語へ移行していったが、ハードウェアの直接操作、特殊命令の使用、性能向上といった目的で今日でもアセンブリ言語が使われている。特にデバイスドライバ組み込みシステムリアルタイムシステムでよく使われている。

歴史的には多数のプログラムがアセンブリ言語だけで書かれてきた。ALGOLの方言であるESPOLで書かれた Burroughs MCP (1961) が登場するまで、オペレーティングシステムはアセンブリ言語で書くのが普通だった。商用アプリケーションもアセンブリ言語で書かれている。例えば、IBMメインフレーム用ソフトウェアの多くはアセンブリ言語で書かれていた。COBOLFORTRANPL/I などが取って代わっていったが、1990年代になってもアセンブリ言語のコードベースを保守し続けていた大企業も少なくない。

初期のマイクロコンピュータでは、アセンブリ言語がよく使われており、OSやアプリケーションもアセンブリ言語で書かれた。これは、リソースの制約が厳しく、メモリやディスプレイのアーキテクチャが特殊だったからである。また、マイクロコンピュータ向けの高水準言語のコンパイラがなかったという面も重要である。また、初期のマイクロコンピュータのユーザは趣味としての使用が主であり、何でも自前で作るという精神もそれに影響していたと見られる。

1980年代から1990年代にかけて、ホームコンピュータZX Spectrumコモドール64AmigaAtari ST など)でもアセンブリ言語がよく使われていた。というのもそれらのBASICは性能が低く、ハードウェアの全機能を利用できないことが多かったためである。例えば、Amigaにはフリーウェアのアセンブリ言語統合開発環境 ASM-One assembler があり、Microsoft Visual Studio に匹敵する機能を備えていた。

Don French が開発した VIC-20 用アセンブラは 1,639 バイトという小ささで、世界一小さいアセンブラと言われている。アドレスをシンボルで表現でき、各種アドレス計算(四則演算、AND、OR、冪乗など)が可能だった[22]

商用製品でアセンブリ言語を使う最大の理由は、使用メモリ量を最小にし、オーバーヘッドを最小にし、性能と信頼性を向上させるためである。1980年代のビジネスソフトでは、例えば表計算ソフト Lotus 1-2-3 などはアセンブリ言語で書かれていた。日本ではなどが該当[23]する。

1990年代に入っても、コンシューマーゲームの多くはアセンブリ言語でプログラムが書かれていた。しかしゲーム内容が複雑化し、プログラムの規模が増大するにつれて、アセンブラでは開発が困難となり、高水準言語による開発が主流となっていった。例えばプレイステーションではGCCが公式のSDKに含まれていて、標準の開発言語はC言語であった[24][25]。この時代のゲーム機は3次元コンピュータグラフィックスの積極的な導入が始まっており、ハードウェア性能も向上したことから、C言語による開発も十分可能となったが、コンパイラの最適化能力が未成熟だったこともあいまって、ハードウェア性能を最大限引き出すにはアセンブリ言語を駆使した手動最適化や細かなチューニングが必要となることも多かった。セガサターンの最高性能を引き出してプレイステーションに対抗するには、アセンブリ言語を使うしかなかったと述べていた業界関係者もいた[26]。ただし一方で、ファミコン時代すでにメタルスレイダーグローリースーパーファミコンMOTHER 2シムシティ[27]、プレイステーションのクラッシュ・バンディクー[28]、開発の一部にLISPが使われていたという話もあり、当時のコンシューマーゲームの分野ではアセンブリ言語やC言語が全てだったというわけではない。

2000年代初頭、マイクロソフトは原始的なプログラマブルシェーダーに対応したDirectX (Direct3D) 8.0をリリースした。このDirect3D 8.0におけるシェーダープログラムは、グラフィックスハードウェアに依存しない中間言語(バイトコード)を出力することのできるアセンブリ言語(シェーダーアセンブラ)を使用して記述するものだった。2001年には世界で初めてプログラマブルシェーダーに対応したコンシューマーゲーム機として初代Xboxが登場したが、このXboxに搭載されていたグラフィックスAPIもDirect3D 8.x相当のカスタマイズ版[29]であり、CPU上で実行するホストプログラム(ゲームアプリケーション本体のコード)はC++を使って記述する一方、GPU上で実行するシェーダープログラムの記述にはアセンブラを使用していた。のちにHLSLCg (C for Graphics) といった高水準シェーディング言語が開発され、HLSLに対応したDirect3D 9.0以降はシェーダープログラムも高水準言語を利用して記述するようになった。Direct3D 10のシェーダーモデル4.0以降は、シェーダーアセンブラではなくHLSLの使用が必須となっている[30]

ゲーム開発の分野に限った話ではないが、C++C#のような、Cよりもさらに高水準の言語が主流になってからも、コンパイラが出力したアセンブリコードを解析して最適化やチューニングの余地を探るといった手法は一般的に行なわれている[31]

現代における使用

アセンブリ言語と高水準言語を比較した有用性と性能についての議論は、これまでよく行われてきた。アセンブリ言語は、後述するように特定の用途で重要な役割を演じている。しかし、一般的には、最適化コンパイラは人手で書かれたアセンブリ言語のコードと同等の性能を発揮すると言われている[32](例外もある[33][34][35])。最近[いつ?]のプロセッサやメモリサブシステムは複雑化してきたため、コンパイラでもアセンブリ言語でも効果的な最適化がますます困難になってきている[36][37]。さらに言えば、プロセッサが高性能化するにしたがって入出力ページングによる遅延が無視できないレベルとなったため、コーディングによる性能追求は多くのプログラマにとって大きな問題ではなくなってきている。

開発者がアセンブリ言語を選択する状況として、次のようなものがある。

  • 限られた資源で単独で動作する小さな実行ファイルが必要とされ、高水準言語のランタイム環境やライブラリが使えない場合。アセンブリ言語を使う状況としては最も一般的である。例えば、電話機のファームウェア、自動車の燃料・点火システム、空調システム、セキュリティシステム、センサーなどがある。
  • デバイスドライバ割り込みハンドラブートコードBIOSPOSTなど、ハードウェアと直接やりとりするコード。
  • コンパイラでは実装されていないプロセッサ固有の命令を使わなければならないプログラム。例えば、暗号のencryptionとdecryptionに使われるビット単位ローテート命令などといった演算を使うためにアセンブリ言語を使用する。
  • コンパイラの最適化では不可能な、SIMD命令を駆使した最適化などを手作業で行いたい場合。
  • 極端な最適化を必要とするプログラム(例えば、プロセッサ集約型アルゴリズム内の内側のループなど)。例えば、コンピュータゲーム開発においては、システムのハードウェア機能を最大限に発揮するようアセンブリ言語を使うことがある。また大規模な科学シミュレーションでも高度に最適化されたアルゴリズムを必要とする。例えば、線型代数学BLAS[33][38]離散コサイン変換(例えばx264のSIMDアセンブリ版[39])がアセンブリ言語で書かれている。
  • 新たなプロセッサや特殊なプロセッサなど、高水準言語が存在しない場合。
  • 次のような正確なタイミングを必要とするプログラム。
  • 高度なセキュリティが要求され、環境を完全に制御する必要がある場合。
  • コンピュータウイルスブートローダ、一部のデバイスドライバなど、ハードウェアと密接に連携するソフトウェア。
  • 監視・トレース・デバッグのための命令セットシミュレータで、追加のオーバーヘッドを最小に保ちたい場合。
  • 次のようなリバースエンジニアリングまたはプログラムファイルの改造。この場合逆アセンブラが併用される。ただしリバースエンジニアリングが許可されていないものを解析・改造することは法律に抵触する場合がある。
    • ソースコードが失われた(または入手できない)バイナリファイルがあるとき、それを改造する場合。あるいはソフトウェアのコピープロテクトを(不正に)解除する場合。
      • 特にソースコードが公開されていないことが多い商用コンピュータゲームの改造。アセンブリ言語レベルでの改造がよく行われている。
  • 自己書き換えコード
  • グラフ電卓向けのソフトウェア開発[40]

なお一方で、最近[いつ?]のコンピュータの命令セットはその多くはどれも似ている。したがって、どれか1つのアセンブリ言語を学ぶだけで、基本概念、どんなときにアセンブリ言語を使用するのが適しているか、高水準言語から効率的な実行コードを生成する方法をある程度は学習できる[41]

より実践的には、コンパイラが生成したコードを確認したい場合といったようなものがある。コンパイラ最適化によって命令の置き換えや省略がされることもあり、高水準言語で書かれたソースコードをデバッグするだけでは原因がつかめない不具合が発生することもある。普段プログラムを記述するときは生産性や移植性を重視して高水準言語を使っていたとしても、プロセッサごとの命令セット仕様や低水準言語の知識も併せて持っているとデバッグの際に役立つ。普段は意識しないような場所であっても、何か理由があっていざという時には腑分けできるということは重要である。これは、たとえ日常の計算には電卓を使っていたとしても、筆算や暗算などを通じて四則演算の基本原則といった算数を学ぶ必要があることに似ている。また、生産性や移植性は劣るものの、高水準言語では不可能な機械語レベルの操作を直接記述することもできるため、「最適化の最後の手段」でもある。

高水準言語との連携

  • 高水準言語の処理系の呼出規約(言語処理系ではなくOSやハードウェアベンダ側で共通化している場合もある)に従うことで、高水準言語と相互にコードを呼び出すことができる。後述のインラインアセンブラなどにより同一のモジュールに埋め込むこともできれば、別モジュールとしてリンケージエディタでリンクすることもある。
  • 多くのコンパイラは、機械語を直接生成するのではなく、アセンブリ言語のコードを生成し、それをアセンブラに通している。人間によるデバッグや最適化などに便利である(機械による最適化には、内部表現を使ったほうが便利なので、あまり意味がない)。その意味ではアセンブリ言語は、目に見えない形ではあるが最も利用頻度の高いプログラミング言語といえるという主張もあるが、その意味では機械語が絶対的に最も利用頻度の高いプログラミング言語である。
  • インラインアセンブラのある言語ないし処理系では、ソース中にアセンブリ言語による記述を含めることができる。例えばLinuxカーネルではその利用が多い。アセンブリ言語と同様の利点が得られるかわりに、やはりアセンブリ言語と同様にプログラミング言語を使う利点(移植性など)が失われる。

主なアセンブラ

Unix系システムでは、アセンブラを as と呼ぶのが一般的だが、実体はそれぞれのOSで異なる。GNUアセンブラを使っているものが多い。

同じ系統のプロセッサであっても、複数のアセンブリ言語の方言が存在する。アセンブラによっては他の方言のアセンブリ言語も使用可能な場合がある。例えば、TASMMASM用コードを入力として受け付け可能だが、逆は不可能である。FASM英語版NASMは文法がほぼ同じだが、サポートしているマクロが異なるため、相互の翻訳は困難である。いずれも基本機能は同じだが、追加機能に差異がある[42]

注釈

  1. ^ IBMはSystem/360から2011年現在まで一貫してアセンブラ言語 (Assembler Language)と 呼んでいる。例:IBM High Level Assembler
  2. ^ MIPSのアセンブラの一部など、(分岐命令のターゲットアドレスの先頭にある機械語命令を対象として)その分岐命令の遅延スロットへの移動を(副作用がない場合に)アセンブラ疑似命令 (.set bopt) の指示に応じて行うものもある。OPTASM(SLR社)という最適化アセンブラもあった。

出典

  1. ^ アセンブリ言語 - コトバンク
  2. ^ Stroustrup, Bjarne, The C++ Programming Language, Addison-Wesley, 1986, ISBN 0-201-12078-X: "C++ was primarily designed so that the author and his friends would not have to program in assembler, C, or various modern high-level languages." - assemblerassembly language の意味で使っている例
  3. ^ Saxon, James, and Plette, William, Programming the IBM 1401, Prentice-Hall, 1962, LoC 62-20615. - assembly program という用語を使っている例
  4. ^ a b David Salomon (1993). Assemblers and Loaders
  5. ^ bit 編集部『bit 単語帳』共立出版、1990年8月15日、8頁。ISBN 4-320-02526-1 
  6. ^ J.DONOVAN, JOHN (1972). systems programming. pp. 59. ISBN 0-07-085175-1 
  7. ^ Beck, Leland L. (1996). “2”. System Software: An Introduction to Systems Programming. Addison Wesley 
  8. ^ a b c d Intel Architecture Software Developer’s Manual, Volume 2: Instruction Set Reference. INTEL CORPORATION. (1999). pp. 442 and 35. http://download.intel.com/design/PentiumII/manuals/24319102.PDF 2010年11月18日閲覧。 
  9. ^ Hyde, Randall. "Chapter 12 – Classes and Objects". The Art of Assembly Language, 2nd Edition. No Starch Press. © 2010.
  10. ^ (John Daintith, ed.) A Dictionary of Computing: "meta-assembler"
  11. ^ Intel Architecture Software Developer’s Manual, Volume 2: Instruction Set Reference. INTEL CORPORATION. (1999). http://download.intel.com/design/PentiumII/manuals/24319102.PDF 2010年11月18日閲覧。 
  12. ^ Evans, David (2006年). “x86 Assembly Guide”. University of Virginia. 2010年11月18日閲覧。
  13. ^ The SPARC Architecture Manual, Version 8”. SPARC, International (1992年). 2011年12月10日時点のオリジナルよりアーカイブ。2012年10月27日閲覧。
  14. ^ Z80 Op Codes for ZINT
  15. ^ Microsoft Corporation. “MASM: Directives & Pseudo-Opcodes”. 2011年3月19日閲覧。
  16. ^ Macros (C/C++) | Microsoft Docs
  17. ^ Concept 14 Macros”. MVS Software. 2009年5月25日閲覧。
  18. ^ Answers.com. “assembly language: Definition and Much More from Answers.com”. 2008年6月19日閲覧。
  19. ^ NESHLA: The High Level, Open Source, 6502 Assembler for the Nintendo Entertainment System
  20. ^ Salomon. Assemblers and Loaders. p. 7. http://www.davidsalomon.name/assem.advertis/asl.pdf 2012年1月17日閲覧。 
  21. ^ The IBM 650 Magnetic Drum Calculator”. 2012年1月17日閲覧。
  22. ^ Jim Lawless (2004年5月21日). “Speaking with Don French : The Man Behind the French Silk Assembler Tools”. 2008年8月21日時点のオリジナルよりアーカイブ。2008年7月25日閲覧。
  23. ^ 松 --- 事実上最初のパソコン用日本語ワープロソフト
  24. ^ Toolchain, libraries and headers relationship - PlayStation Development Network
  25. ^ What were PS1 and N64 games written in? : gamedev
  26. ^ SegaBase Volume 6 - Saturn”. Eidolon's Inn (2008年1月10日). 2014年7月2日時点のオリジナルよりアーカイブ。2013年6月27日閲覧。
  27. ^ Lispによるリターゲッタブルコードジェネレータの実装 (PDF) Archived 2008年8月20日, at the Wayback Machine.
  28. ^ OOエンジニアの輪! ~ 第 21 回 川合史朗 さんの巻 ~ | オブジェクトの広場
  29. ^ NVIDIA Xbox GPU Specs | TechPowerUp GPU Database
  30. ^ Using Shaders in Direct3D 10 - Win32 apps | Microsoft Docs
  31. ^ [CEDEC]「FINAL FANTASY XV」の最適化はこうして行われた - GamesIndustry.biz Japan Edition
  32. ^ Rusling, David A.. “The Linux Kernel”. 2012年3月11日閲覧。
  33. ^ a b Writing the Fastest Code, by Hand, for Fun: A Human Computer Keeps Speeding Up Chips”. New York Times, John Markoff (2005年11月28日). 2010年3月4日閲覧。
  34. ^ Bit-field-badness”. hardwarebug.org (2010年1月30日). 2010年2月5日時点のオリジナルよりアーカイブ。2010年3月4日閲覧。
  35. ^ GCC makes a mess”. hardwarebug.org (2009年5月13日). 2010年3月16日時点のオリジナルよりアーカイブ。2010年3月4日閲覧。
  36. ^ Randall Hyde. “The Great Debate”. 2008年6月16日時点のオリジナルよりアーカイブ。2008年7月3日閲覧。
  37. ^ Code sourcery fails again”. hardwarebug.org (2010年1月30日). 2010年4月2日時点のオリジナルよりアーカイブ。2010年3月4日閲覧。
  38. ^ BLAS Benchmark-August2008”. eigen.tuxfamily.org (2008年8月1日). 2010年3月4日閲覧。
  39. ^ x264.git/common/x86/dct-32.asm”. git.videolan.org (2010年9月29日). 2012年3月4日時点のオリジナルよりアーカイブ。2010年9月29日閲覧。
  40. ^ 68K Programming in Fargo II”. 2008年7月2日時点のオリジナルよりアーカイブ。2008年7月3日閲覧。
  41. ^ Hyde, Randall (1996年9月30日). “Foreword ("Why would anyone learn this stuff?"), op. cit.”. 2010年3月25日時点のオリジナルよりアーカイブ。2010年3月5日閲覧。
  42. ^ Randall Hyde. “Which Assembler is the Best?”. 2007年10月18日時点のオリジナルよりアーカイブ。2007年10月19日閲覧。

参考文献

  • Jonathan Bartlett: Programming from the Ground Up. Bartlett Publishing, 2004. ISBN 0-9752838-4-7
    Also available online as PDF[リンク切れ]
  • Robert Britton: MIPS Assembly Language Programming. Prentice Hall, 2003. ISBN 0-13-142044-5
  • Paul Carter: PC Assembly Language. Free ebook, 2001.
    Website
  • Jeff Duntemann: Assembly Language Step-by-Step. Wiley, 2000. ISBN 0-471-37523-3
  • Randall Hyde: The Art of Assembly Language. No Starch Press, 2003. ISBN 1-886411-97-2
  • Peter Norton, John Socha, Peter Norton's Assembly Language Book for the IBM PC, Brady Books, NY: 1986.
  • Michael Singer, PDP-11. Assembler Language Programming and Machine Organization, John Wiley & Sons, NY: 1980.
  • Dominic Sweetman: See MIPS Run. Morgan Kaufmann Publishers, 1999. ISBN 1-55860-410-3
  • John Waldron: Introduction to RISC Assembly Language Programming. Addison Wesley, 1998. ISBN 0-201-39828-1

関連項目

外部リンク