
コンピューティングにおいて、マシンコードは、プログラム可能なインターフェースを介してコンピュータの中央処理装置(CPU)を制御するためにエンコードされ、構造化されたデータ です。コンピュータプログラムは、主にマシンコード命令のシーケンスで構成されています。[ 1 ]マシンコードは、CPUが直接解釈する言語であるため、ホストCPUに対してネイティブに分類されます。 [ 2 ]一部のソフトウェアインタープリタは、解釈するプログラミング言語を仮想マシンコード(バイトコード)に変換し、 Pコードマシンで処理します。
機械語命令は、CPUに次のような特定のタスクを実行させる。
命令セットアーキテクチャ(ISA)はCPUへのインターフェースを定義するもので、x86やARMといったCPU設計のグループやファミリによって異なります。一般的に、あるファミリと互換性のあるマシンコードは他のファミリとは互換性がありませんが、例外もあります。VAXアーキテクチャは、オプションでPDP-11命令セットをサポートしています。IA -64アーキテクチャは、オプションでIA-32命令セットをサポートしています。また、PowerPC 615は、 PowerPC命令とx86命令の両方をネイティブに処理できます。

アセンブリ言語は、人間が読めるソースコードからマシンコードへの比較的直接的なマッピングを提供します。アセンブリ言語のソースコードは、ニーモニックやラベルとしてマシンコードの数値コードを表します。[ 3 ]例えば、x86NOPプロセッサのアセンブリでは、x86 アーキテクチャのオペコード0x90 をマシンコードで表します。マシンコードでプログラムを書くことは可能ですが、面倒でエラーが発生しやすくなります。そのため、プログラムは通常アセンブリ言語で、またはより一般的には高水準プログラミング言語で書かれます。
機械命令は、機械の命令セットに指定されたフォーマットに基づいて、ビットのパターンとして操作をエンコードします。 [ nb 1 ] [ 4 ]
命令セットはさまざまな点で異なります。セット内の命令はすべて同じ長さである場合もあれば、異なる命令の長さが異なる場合もあります。命令の長さは、アーキテクチャのワードサイズよりも小さい場合、同じサイズの場合、または大きい場合があります。命令の数は比較的小さい場合もあれば、大きい場合もあります。命令は、アーキテクチャのワード境界などの特定のメモリ境界にアラインメントする必要がある場合とない場合があります。[ 4 ]
命令セットは、コンピュータのデジタル論理レベルの回路を実行する必要があります。デジタルレベルでは、プログラムはコンピュータのレジスタ、バス、メモリ、ALU、およびその他のハードウェアコンポーネントを制御する必要があります。[ 5 ]コンピュータのアーキテクチャ機能を制御するために、機械語命令が作成されます。機械語命令を使用して制御される機能の例は次のとおりです。
指導形式の基準は以下のとおりです。
アドレス フィールドのサイズを決定することは、スペースと速度のどちらかを選択することになります。[ 9 ]一部のコンピュータでは、アドレス フィールドのビット数が小さすぎて、物理メモリ全体にアクセスできない場合があります。また、仮想アドレス空間も考慮する必要があります。別の制約として、アドレスを構成するために使用されるレジスタのサイズに制限がある場合もあります。アドレス フィールドが短いほど命令の実行速度は速くなりますが、命令フォーマットを設計する際には、他の物理的特性も考慮する必要があります。
命令は、汎用命令と特殊命令の2種類に分類できます。特殊命令は、コンピュータ固有のアーキテクチャ機能を活用します。汎用命令は、すべてのコンピュータに共通するアーキテクチャ機能を制御します。[ 10 ]
汎用命令制御:
可変長命令セットを持つプロセッサアーキテクチャ[ 11 ] ( Intelのx86プロセッサファミリーなど) では、クラスカルカウントとして知られる制御フロー再同期現象の制限内で、オペコードレベルのプログラミングによって、2つのコードパスが共通のオペコードシーケンスの断片を共有するように、結果のコードを意図的に配置できる場合があります。[ nb 2 ]これらは、重複命令、重複オペコード、重複コード、重複コード、命令分割、または命令の中間へのジャンプと呼ばれます。[ 16 ] [ 17 ] [ 18 ]
1970年代と1980年代には、メモリ領域を節約するために、命令の重複が使用されることがありました。一例として、MicrosoftのAltair BASICのエラーテーブルの実装があり、そこではインターリーブされた命令が命令バイトを相互に共有していました。[ 19 ] [ 11 ] [ 16 ]この手法は今日ではほとんど使用されていませんが、ブートセクタに収まる必要があるブートローダーの実装など、バイトレベルでのサイズの極端な最適化が必要な分野では、依然として必要になる場合があります。[ nb 3 ]
また、逆アセンブルや改ざんに対する対策として、コード難読化技術として使用されることもあります。[ 11 ] [ 14 ]
この原理は、複数の命令セット非互換プロセッサプラットフォーム上で実行する必要のあるファットバイナリの共有コードシーケンスにも使用されています。 [注2 ]
このプロパティは、既存のコードリポジトリ内のガジェットと呼ばれる意図しない命令を見つけるためにも使用され、リターン指向プログラミングでは、return-to-libc 攻撃などのエクスプロイトに対するコードインジェクションの代替として使用されます。[ 20 ] [ 11 ]
一部のコンピュータでは、アーキテクチャのマシン コードは、マイクロ コードと呼ばれるさらに基本的な基盤レイヤーによって実装されており、基盤となるデータ フローが大きく異なるコンピュータの異なるモデル間で共通のマシン 言語インターフェースを提供します。これは、異なるモデル間でマシン 言語プログラムを移植しやすくするために行われます。 [ 21 ]この使用例としては、IBM System/360ファミリーのコンピュータとその後継機が挙げられます。[ 22 ]


1954年に発表されたIBM 650は、命令とデータが磁気ドラムに格納された10進数ワードアドレス方式のコンピュータでした。各ワードは10桁のプラス記号で構成されていました。命令はワードを2桁の操作コード、処理対象のデータワードの4桁のアドレス、および次に実行する命令の4桁のアドレスに分割しました。2番目のアドレスにより、前の命令が完了した後にドラムが配置される位置の近くに命令を配置することが可能になり、これは最適化と呼ばれる手法でした。[ 23 ]
命令コードの物理的な表を用いることで、機械語でプログラムを作成することは十分に可能でした。IBMは、各メモリ位置を示すグリッドを備えたフォームを提供し、プログラマがどの位置がまだ使用可能かを把握できるようにしました。1カードあたり1命令のフォーマットがあり、これをマシンに直接ロードして実行することができました。その後、IBMはシンボルアドレス指定を可能にし、大まかな最適化処理も行うアセンブラ(SOAP)を導入しました。
IBM 704、709、704x、709xは、各命令ワードに1つの命令を格納します。IBMは、左からビットをS、1、...、35と番号付けします。ほとんどの命令は、次の2つの形式のいずれかです。
IBM 7094および 7094 II を除くすべての機種では、A、B、C の 3 つのインデックス レジスタがあります。タグに複数の 1 ビットを指定してインデックスを指定すると、選択されたインデックス レジスタの論理 ORが減算され、タグに複数の 1 ビットを指定してロードすると、選択されたすべてのインデックス レジスタがロードされます。7094 および 7094 II には 7 つのインデックス レジスタがありますが、電源投入時はマルチタグ モードになっており、以前の機種と互換性のある方法で 3 つのインデックス レジスタのみを使用します。残りの 4 つのインデックス レジスタにアクセスするには、マルチタグ モード解除 ( LMTM ) 命令が必要です。
実効アドレスは通常 YC(T) であり、C(T) はタグが 0 の場合は 0、マルチタグモードでは選択されたインデックスレジスタの論理和、マルチタグモードでない場合は選択されたインデックスレジスタのいずれかになります。ただし、インデックスレジスタ制御命令の実効アドレスは単に Y です。
両方のビットが1のフラグは間接アドレス指定を選択します。間接アドレスワードには、タグとYフィールドの両方が含まれます。
これらのマシンには、転送(分岐)命令に加えて、条件付きで1つまたは2つのワードをスキップするスキップ命令があります。たとえば、ストレージとアキュムレータの比較(CAS)は3方向比較を行い、結果に応じてNSI、NSI+1、またはNSI+2に条件付きでスキップします。
MIPSアーキテクチャは、命令が常に32ビット長であるマシンコードの具体的な例を示しています。[ 24 ]: 299命令の一般的なタイプは、上位6ビットであるop (演算)フィールドによって指定されます。Jタイプ(ジャンプ)命令とIタイプ(即値)命令は、 opによって完全に指定されます。Rタイプ(レジスタ)命令には、正確な演算を決定するための追加のfunct(関数)フィールドが含まれます。これらのタイプで使用されるフィールドは次のとおりです。
rs、rt、rdはレジスタオペランドを示し、shamtはシフト量を示し、アドレスフィールドまたは即値フィールドにはオペランドが直接含まれる。[ 24 ]: 299-301
例えば、レジスタ1とレジスタ2を加算し、結果をレジスタ6に格納すると、次のようにエンコードされます。[ 24 ] : 554
[ op | rs | rt | rd |shamt| funct] 0 1 2 6 0 32 10進数 000000 00001 00010 00110 00000 100000 バイナリ
レジスタ3に示された位置から68セル後のメモリセルから取得した値をレジスタ8にロードします。[ 24 ] : 552
[ op | rs | rt | address/immediate ] 35 3 8 68 十進数 100011 00011 01000 00000 00001 000100 バイナリ
アドレス1024へジャンプ: [ 24 ] : 552
[ op | ターゲットアドレス ] 2 1024 十進数 000010 00000 00000 00000 10000 000000 バイナリ
マシンコードはバイトコードと似ているようでいて、根本的に異なります。マシンコードと同様に、バイトコードも通常はソースコードから(コンパイラによって)生成されます。しかし、マシンコードとは異なり、バイトコードはCPUによって直接実行することはできません。ただし、Pascal MicroEngineやJavaプロセッサのように、プロセッサがマシンコードとしてバイトコードを使用するように設計されている場合は例外です。バイトコードがソフトウェアインタープリタによって処理される場合、そのインタープリタは仮想マシンであり、バイトコードはそのインタープリタのマシンコードとなります。
実行中、マシンコードは一般的にRAMに格納されますが、一部のデバイスではROMからの実行もサポートされています。いずれにせよ、パフォーマンスを向上させるために、コードはより専用のメモリにキャッシュされる場合もあります。アーキテクチャによっては、命令とデータ用に異なるキャッシュが存在する場合があります。[ 25 ]
プロセスの観点から見ると、マシンコードはコード空間、つまりそのアドレス空間の指定された部分に存在します。マルチスレッド環境では、1つのプロセスの異なるスレッドがデータ空間とともにコード空間を共有するため、プロセス切り替えと比較してコンテキスト切り替えのオーバーヘッドが大幅に削減されます。 [ 26 ]
機械語は一般的に人間が読めるものではないと考えられており、[ 27 ]ダグラス・ホフスタッターはそれをDNA分子の原子を調べることに例えている。[ 28 ]しかし、機械語を理解するのに役立つさまざまなツールや方法がある。
逆アセンブルは機械語をアセンブリ言語にデコードするが、これはアセンブリ命令が機械語命令に1対1で対応できることが多いため可能である。[ 29 ]
デコンパイラは機械語を高級言語に変換するが、その結果は比較的難解(理解しにくい)になる可能性がある。
プログラムにはデバッグシンボル(ネイティブ実行ファイルに埋め込まれているか、別のファイルに格納されている)を関連付けることができ、それによって外部ソースコードにマッピングできます。デバッガはこれらのシンボルを読み取り、プログラマがプログラムを対話的にデバッグできるように支援します。例としては、以下のようなものがあります。