機械語
機械語(きかいご)またはマシン語は、プロセッサが直接理解し実行できるバイナリをプログラミング言語として扱う場合の呼称である。
人が直接読み書きする場合は、バイナリエディタなどで二進法や十六進法表現を利用することが多く、ワード長が3の倍数の場合などでは八進法、十進方式のコンピュータで十進法を使うこともある。
低水準言語のうちでも最も低水準のプログラム言語であり、もはやそれは言語ではないとされることもある。
また、低水準ではないが、仮想機械#プロセス仮想機械や抽象機械などが実行するものも機械語とされる。
目次 |
概要 [編集]
プロセッサは主記憶装置にある機械語で書かれたプログラムを読み取り、解析、実行、出力することで処理を行っている。すべてのプログラミング言語で書かれたプログラムは、最終的にこの機械語で実行される。機械語でのプログラミングには、機械語とほぼ1対1に対応するニーモニックを用いたアセンブリ言語を使うのが一般的である。これに対し人力で機械語にすることをハンドアセンブルという。
機械語を利用する理由は、以前は次のようなものであった。
- アセンブラが存在しないか高価なため購入できない、クロスアセンブラであるため別のコンピュータが必要、など
- コンピュータの性能が低いうえにBASICインタプリタしか備えていないなどで、必要な性能を得るため
- コンパイラの研究が途上で高性能なコードが生成されないため
今日では、機械語を使わずとも十分なほどコンピュータは高性能になり、またコンパイラの研究も進んでよくできたコンパイラであれば場合によっては人より高性能なコードを生成するようになった。このため、機械語を直接扱うのは、コンパイラがまだ対応していない新機能などを使う場合、アセンブラを作ったりする場合、プロセッサのバグに当たった場合、コアダンプを解析する場合、諸事情などで逆アセンブルなどリバースエンジニアリングをおこなう場合、などに限られてきている。
機械語と互換性 [編集]
一概に機械語と言っても、静的リンクによるコンパイラ方式では機械語へコンパイルされただけでリンクされていない「オブジェクト・コード」の状態ではまだプロセッサは実行することができず、リンカによって外部参照された関数名などからライブラリのオブジェクト・コードを付加したりすることでプロセッサが実行可能な「ロード・モジュール」(実行可能形式)となる。
また、命令セットその他の仕様が異なるプロセッサの機械語プログラムは実行できない。同じ機械語プログラム実行できることを互換性があるという。
たとえば、Pentium系列とPowerPC系列の双方で動くプログラムが存在しないのは、命令セットに互換性が無いからである。たとえ同じ系列だとしても、新しい世代のプロセッサのために作ったプログラムは古い世代のプロセッサでは動かないこともある。機械語プログラムがそのまま動くか否か、という互換性を「バイナリ互換性」といい、プロセッサの仕様だけでなく、コンピュータの他の部分の仕様やファームウェアやオペレーティングシステムなども関わる。
ただし、注意深く機械語命令を使用することによって異なるアーキテクチャで動作するプログラムを書くことは不可能ではなく、Polyglot(en:Polyglot (computing))の極端な1ジャンルともいえる。PC-98とX68k両対応のブートセクタ[1]、記念すべき第1回IOCCCの入選作のひとつでmullenderによるPDP-11とVAX両対応プログラム[2]など。
機械語とアセンブリ言語 [編集]
ファイルシステムが存在するコンピュータでは、機械語で書かれたプログラムは、実行ファイル(実行可能なバイナリファイル)として存在することが多い。21世紀現在、使用されているすべてのコンピュータは、実行すべき命令列を2進数データの組合せで論理的意味を持つ機械語のファイルとして読み込んで実行している。機械語で書かれたプログラムファイルを人が読めるようにするには、2進数を4ビットずつ16進数に変換して表示することが多く、この"0"から"F"までの文字が羅列された印刷出力は「ダンプリスト」と呼ばれる。この状態でプログラムの内容が分かるような人はほとんど存在しないが、初期のコンピュータ技術者の中には、特定のCPUの機械語による命令体系を覚えていて、16進数の状態で内容を読み取ることが可能な者もいる。
機械語を解読する場合には、オブジェクトファイルを読み込んで、対応するニーモニックとラベルを出力する逆アセンブラが利用され、この行為をディスアセンブルという。またアセンブリ言語を機械語に変換するアセンブラを用いずに、機械語とニーモニックの対応表を参考にニーモニックと番地・定数から機械語を表す16進数を手作業で作る行為をハンドアセンブルという。アセンブラが作られていないか、あっても高価だった時代には、ハンドアセンブルによりプログラムを開発・保守することが一般的であった。
構成 [編集]
一般的な機械語プログラムは以下のような構成となっている。
- 命令部(オペレーションコード、あるいはオペコード)
- CPUに処理をさせるための命令の番号を記録している。
- アドレス部(オペランド)
- 情報として利用するデータが格納されている、あるいは結果の記録先のレジスタやメモリアドレス、ジャンプ先などを示す、後述のイミディエイト値もこれに含めることもある。命令によって、個数や長さが異なる。オペランドの数について、0アドレス方式、1アドレス方式、2アドレス方式、3アドレス方式がある。0アドレス方式はオペレーションコードだけで、オペランドは存在しない。
- イミディエイト値
- オペランドの一部に含めることもある。演算に使用する整数値などのデータが命令に引き続いて(イミディエイト)置かれているもの。即値とも。
- データ部
- 以上は実行される部分だが、これは実行されない部分である。プログラムで使用するデータのうち前述のイミディエイト値に収まらないもの、文字列などのようなデータ、グローバル変数(機械語やアセンブリ言語プログラミングの用語ではワークエリア)のための領域、など。通常あるていどまとめて置かれる。当然ながら命令として解釈しようとするとちんぷんかんぷんである。
thumb命令以前のARMなど一部の固定長命令方式のプロセッサを除き、命令の種類によってアドレス部やデータ部、そして中には命令部までも長さが変わることで、機械語の命令語長は可変になっている。異なる命令語長やいかなる命令体系であっても、最初にCPUが読み込むのは命令部であり、固定語長のものを除けば命令部からアドレス部やデータ部、そして拡張命令部の配置を読み取る。このため、読み込み位置が1バイトずれれば機械語の命令はそれ以降のすべての命令が正しく読み込まれず意味を失う。
CPUによる仕様の差異 [編集]
上記類似点の範囲でのCPUごとの機械語の仕様の差異には、以下のようなものが挙げられる。
- CPUが理解できる命令の種類や数が異なる(CISC、RISC、VLIW)
- 命令の長さが異なる(CISCとRISCとでは長さが異なることがある。また、同じアーキテクチャでも、命令のビット数の違いも影響する)
- 命令部の命令番号が一致しない
- 同じ処理を行う命令でも処理結果が異なる
- 演算方法が異なる(レジスタ - レジスタ間演算やメモリ - レジスタ間演算の違い。RISCでは後者の演算ができない)
- データの記録方法が異なる(エンディアンやアラインメントの相違)
- 実行形式のバイナリファイルの記録形式が異なる(PE、COFF、ELFなど)
脚注 [編集]
- ^ 電脳インストーラ2
- ^ http://www.ioccc.org/years.html#1984_mullender C言語じゃないか! と思った人はソースコードを見ること。なおのちの回ではコンテストのルールが変更されこのようなプログラムは禁止されている。