低水準プログラミング

この教科書は、コンピューターシステムの基本的な構成要素であるオペレーティングシステムに焦点を当てています。読者には、プロセス管理、メモリ管理、ファイルシステム、デバイス管理、セキュリティ、ケーパビリティ、プロセス間通信など、幅広いトピックを網羅的に理解することが期待されます。オペレーティングシステムがシステムの安定性、効率性、およびセキュリティに与える影響を考察し、現代のコンピューティング環境において不可欠な役割を果たしていることを強調します。著者は基本的な概念をわかりやすく説明し、実際のシステムに対する応用に焦点を当てています。この教科書は、学生、研究者、そしてプロフェッショナルがオペレーティングシステムの理論と実践を深く理解し、実用的なスキルを磨くための道しるべとなることを意図しています。
現代社会において、コンピューターシステムは我々の日常生活やビジネス活動において不可欠な存在となっています。この章では、コンピューターシステムの根幹をなす基本構成要素に焦点を当て、その洗練された構造が如何にして機能するかを探求します。
プロセッサ、メモリ、デバイスなどのハードウェアは、コンピューターシステムの根幹をなす要素であり、計算とデータ処理の基盤を提供しています。プロセッサは高度な演算能力を有し、プログラムの実行やデータ処理を担当します。メモリは、プロセッサが効率的にアクセスできるようにデータや命令を一時的に保持し、効率的な処理を支えます。デバイスは、入出力を制御し、外部機器との連携を担当します。これらのハードウェアコンポーネントが連携することで、コンピューターシステムは多様なタスクを遂行し、我々の日常生活に欠かせない存在となっています。
ソフトウェアは、ハードウェアを効果的に制御し、様々なタスクを実行します。オペレーティングシステムは、ハードウェア資源を管理し、アプリケーションソフトウェアが効果的に動作する環境を提供します。アプリケーションソフトウェアは、ユーザーのニーズに合わせて機能し、データの処理や情報の提供などを行います。ソフトウェアの協力により、ユーザーエクスペリエンスが向上し、リソースが最適に利用されます。
ネットワーク技術や通信プロトコルは、異なるコンピューターシステム同士を結びつけ、情報の共有と連携を可能にします。これにより、データの移動やリモートアクセスが実現され、情報社会の基盤を支えています。通信機構によって、地理的な制約を超えてリソースや情報を利用できるようになり、グローバルなコネクティビティが形成されます。これらの要素が緊密に連携し、複雑な機能を実現しているコンピューターシステムの進化を探求します。
オペレーティングシステム(OS)は、コンピューターシステムにおいて不可欠な仲介者としての役割を果たしています。その基本的な役割は多岐にわたり、コンピュータの効率的な運用とユーザーエクスペリエンスの向上を担保します。この節では、オペレーティングシステムが果たす主要な役割に焦点を当て、その進化と歴史的変遷を明らかにします。
オペレーティングシステムはその発展の中で、様々な進化と挑戦に直面してきました。時代とともに変わる技術や要件に順応し、新たな機能やアーキテクチャが取り入れられてきました。
以下はその進化の一端を探るものです。
これらの進化と挑戦は、オペレーティングシステムが未来の技術とニーズに対応し続けるための基盤となっています。将来的な展望では、人工知能、量子コンピューティング、エッジコンピューティングなどが新たな課題となり、オペレーティングシステムはこれらの分野においてどのように進化していくかが注目されています。
オペレーティングシステムの理解には、複数の基本的な概念や用語への理解が欠かせません。これらの用語の統一的な理解が、後の章で深化する知識の基盤となります。
これらの基本的な概念と用語への理解が、オペレーティングシステムにおけるさまざまな機能やメカニズムを理解する上での出発点となります。深い知識の構築に向けて、これらの概念を綿密に理解していきましょう。
プロセスは、コンピュータシステム内で実行されるプログラムの実行単位です。それはプログラムのコード、データ、実行状態など、実行に必要な情報を含みます。プロセスには一意のプロセスIDが割り当てられ、オペレーティングシステムはこれらのプロセスを管理し、リソースの割り当てや制御を担当します。
プロセスのスケジューリングと管理 プロセスのスケジューリングは、複数のプロセスが競合する場合に、どのプロセスを優先して実行するかを決定する仕組みです。スケジューリングアルゴリズムは、公平性、優先度、待ち時間などの要因を考慮して、効率的かつ公正なリソース利用を実現します。オペレーティングシステムはまた、プロセスの生成、終了、中断などを管理し、メモリやCPUの効率的な利用を促進します。
複数のプロセスが同時に実行される際に、これらのプロセスが協調して動作するためには、プロセス同期と通信が必要です。同期は、プロセス間での相互の進捗状況を合わせ、競合状態やデータ不整合を防ぎます。通信は、プロセス間でデータを受け渡し、情報を共有するための仕組みです。これにより、連携してタスクを達成することが可能となります。
オペレーティングシステムは、異なるプロセス間でデータを共有し、同期を図るためにさまざまな機構を提供します。共有メモリ、パイプ、メッセージキュー、セマフォ、モニタなどがその例です。これらの機構を適切に使用することで、プロセス間の協調動作やデータの安全な受け渡しが可能となります。 プロセス管理はオペレーティングシステムの中核的な機能であり、効率的で安定したシステム運用を支えます。各概念や機構の理解は、システム設計やプログラミングにおいて重要です。
BSD Socket(Berkeley Software Distribution Socket)は、ネットワーク通信のためのAPI(アプリケーションプログラミングインターフェース)です。BSD UNIXオペレーティングシステムにおいて開発され、後に標準的なネットワークプログラミングのインターフェースとして多くのオペレーティングシステムで採用されました。BSD Socketは、ソケットを用いてプロセス間通信やネットワーク通信を実現するための機能を提供します。
BSD UNIXではSocketを使ってパイプなどのIPC(プロセス間通信)も再実装されました。BSD UNIXが開発された際、Socketを通じたネットワーク通信だけでなく、プロセス間通信の手段としてもSocketが利用され、パイプなどが再構築されました。
BSD Socket APIはその後、SystemVを含む多くのUNIX系オペレーティングシステムに採用され、ネットワークとIPCの両方で幅広く使用されるようになりました。
SystemV STREAMS(StreamS)は、UNIX System Vオペレーティングシステムで導入された通信機構です。STREAMSは、モジュール化された通信プロトコルスタックを提供し、柔軟で効率的なデータの受け渡しを可能にします。これは、プロセス間通信やデバイス制御において、標準的なインターフェースとして使用されました。 STREAMSはUNIXの派生版や一部の商用UNIXオペレーティングシステムでサポートされています。
オペレーティングシステムのメモリ管理は、コンピュータのメモリ資源を効果的に管理する仕組みです。これにより、複数のプログラムが同時に実行され、物理メモリを適切に利用できます。メモリ管理はプロセスの割り当てや解放、ページング、セグメンテーション、仮想メモリの導入などを含み、システムの安定性と性能を確保します。
メモリ管理は、コンピューターシステムにおける重要な側面であり、メモリは階層的な構造を有しています。この階層には、コンピュータがデータを処理する際に使用される異なるタイプのメモリが含まれます。最も高速で直接アクセス可能なのがレジスタであり、次にキャッシュ、主記憶(RAM)、そして補助記憶(ハードディスクなど)が続きます。この章では、各メモリ階層がどのように機能し、効果的に管理されるかを探求します。
レジスタはCPU内部にあり、非常に高速でありながらも容量が非常に小さいため、プロセッサが即座にアクセスできるデータを格納します。キャッシュは主記憶とプロセッサ間のデータ転送を高速化するための中間的なメモリであり、主記憶はプロセッサが直接アクセスする大容量のメモリです。補助記憶は永続的なデータの保存やプログラムの読み込みに用いられます。
仮想記憶は、主記憶装置と補助記憶装置から構成される記憶領域であり、補助記憶装置を安価で大容量のデバイスとして使用することで、プログラマが仮想記憶上で大容量の主記憶装置が実装されているかのようにプログラミングを行うことができる概念です。
ページングとセグメンテーションは、仮想記憶の管理手法であり、物理メモリの効率的な利用を目指しています。 ページングでは、メモリを固定サイズのページに分割し、プログラムの実行に必要なページだけを動的に読み込みます。これにより、物理メモリの空間が節約され、必要なページだけが物理メモリに配置されます。ページングは、デマンドページングの手法を使用し、プロセスがアクセスしようとするページが物理メモリに存在しない場合にページフォルトが発生し、必要なページが動的に読み込まれます。 一方、セグメンテーションでは、プログラムを論理的なセグメントに分割し、必要なセグメントだけを物理メモリに読み込むことでメモリを効果的に利用します。各セグメントは論理的なまとまりを持ち、柔軟なメモリ管理が可能です。 これらの手法は、コンピュータシステムのメモリ管理において重要であり、効率的なリソース利用と性能向上に寄与しています。
仮想記憶は、物理メモリを補完するための概念であり、プロセスにとっては拡張されたメモリ空間を提供します。通常、プロセスは必要なだけのメモリを仮想的に確保できるため、物理メモリよりも広範なメモリアクセスが可能です。しかし、すべての仮想記憶が物理メモリに常時格納されているわけではありません。
ページフォルトは、プログラムが物理メモリにアクセスしようとした際に発生します。もし必要なページが物理メモリに存在しない場合、オペレーティングシステムはページフォルトを検知し、必要なページをディスクや他の補助記憶装置から読み込みます。これにより、物理メモリの限られたスペースに必要なページだけを動的に配置することが可能となります。
ページフォルトのハンドリングは、仮想記憶の有効活用と効率的なメモリ使用を実現します。オペレーティングシステムは必要なページを要求された時点で読み込むため、大きなプログラムやデータセットを処理する際にも物理メモリを効果的に活用できます。
デマンドページング方式の仮想記憶は、ページングとPMMU(Paged Memory Management Unit)との連携によって実現されます。
以下に、その仕組みについて簡単に説明します。
プロセスが初めてアクセスしようとするメモリ領域に対して、そのページが物理メモリに存在しない場合にページフォルトが発生します。ページフォルトが発生すると、オペレーティングシステムは必要なページを補完的に物理メモリに読み込みます。
仮想記憶、デマンドページング、PMMUが組み合わさることで、大容量の仮想メモリを有効に利用しながら、物理メモリを必要に応じて動的に確保し、効率的にプロセスを実行することが可能となります。
仮想記憶の実装には、デマンドページングとPMMU以外にもいくつかのアプローチが存在します。以下に、その一部を紹介します。
これらの手法は、システムの要件や特性によって異なる利用があり、適切な組み合わせが選択されます。
初期の仮想記憶の実装例として、Atlasコンピュータが挙げられます。Atlasは、1960年代初頭にイギリスのマンチェスター大学で開発された大型のコンピュータシステムで、仮想記憶の概念を実現しました。Atlasの仮想記憶システムは、当時のコンピュータシステムと比較して先進的で、ページング・ページテーブル・プロセス間の分離などの特徴がありました。 {{See|[[#Atlas Supervisor (1962)}}}}
Atlasの仮想記憶システムは、その後のコンピュータアーキテクチャの発展に影響を与え、ページングや仮想記憶の考え方が広く受け入れられる基盤を築きました。
MULTICS(Multiplexed Information and Computing Service)は、仮想記憶を採用した初期の商用オペレーティングシステムの一つです。MULTICSは、General Electric(GE)、Bell Labs(AT&T)、およびMIT(Massachusetts Institute of Technology)の共同開発プロジェクトとして始まり、GE-645メインフレームコンピュータ上で動作しました。
MULTICSは、セキュリティや拡張性の向上を含む様々な革新的な機能を持っていましたが、その中でも特に注目されたのが仮想記憶の導入でした。MULTICSの仮想記憶は、物理メモリよりも大きな仮想メモリ空間をプログラムに提供し、必要な部分だけを物理メモリに配置することで、大規模なプログラムや複数のプロセスを同時に実行できるようになりました。
MULTICSの仮想記憶は、ページングとセグメンテーションを組み合わせた方式を採用していました。これにより、プログラムやデータを効率的に管理し、ユーザーに対して広い仮想アドレス空間を提供できました。MULTICSの仮想記憶は、その後のオペレーティングシステムにおける仮想記憶の発展に影響を与えました。
商用で最初に導入された仮想記憶システムの一つは、IBMの「System/360 Model 67」が含まれます。このシステムは、1966年に導入されました。System/360 Model 67は、プログラムが物理メモリに収まりきらなくても実行できるようにするために、仮想記憶の概念を採用していました。
System/360 Model 67の仮想記憶システムは、プログラムが実際の物理メモリよりも大きなサイズであっても実行でき、必要な部分だけを物理メモリに読み込む仕組みを持っていました。これにより、大規模で複雑なプログラムを効率的に実行できるようになりました。
メモリ保護は、プロセスがメモリを読み書きできるかどうかを管理します。アクセス権の設定やセグメンテーションにおけるセグメント保護など、様々な手法が存在します。これらの手法により、オペレーティングシステムはプロセスが正しくメモリにアクセスできるように制御します。
アクセス権は通常、読み取り専用、読み書き可能、実行可能などの形式で指定され、各プロセスが必要な権限を持つかどうかを管理します。セグメンテーションにおいては、各セグメントに対して保護フラグが設定され、プロセスはそれに基づいてメモリへのアクセスを制御します。これにより、異なるプロセスがお互いのメモリを不正にアクセスすることを防ぎ、システム全体のセキュリティを確保します。
以下は、OSのメモリ管理に関連する用語の一覧です。
オペレーティングシステムのファイルシステムは、データを整理して保存する仕組みです。ファイルやディレクトリを階層的に管理し、アクセスや保護機能を提供します。これにより、ユーザーはデータを構造的に保存・取得でき、プログラムやシステムが必要な情報にアクセスできます。ファイルシステムはデータの整合性と永続性を保ち、効率的なデータ管理を実現します。
オペレーティングシステムのデバイス管理は、コンピュータ内外のハードウェアデバイスとの効率的な通信を担当します。デバイスドライバを介し、異なるデバイスとのインタラクションを標準化し、アプリケーションやユーザーがハードウェアを利用できるようにします。デバイス管理はデバイスの初期化、資源割り当て、エラーハンドリングなどを通じてシステムの正確かつ円滑な動作を確保します。
オペレーティングシステムのセキュリティは、機密性、整合性、可用性を保護し、不正なアクセスからシステムを守ります。一方で、ケーパビリティはユーザーやプロセスが持つ権限や操作可能な範囲を制御し、機能の適切な利用を促進します。セキュリティとケーパビリティの統合は、システム全体の信頼性と安定性を確保します。
オペレーティングシステムのプロセス間通信(IPC)は、プロセス間で情報をやり取りするメカニズムで、共有メモリやメッセージパッシングなどが利用されます。ネットワーキングは、複数のシステムが通信してリソースを共有し、分散処理やデータ交換を可能にします。これらはオペレーティングシステムが異なるプロセスやシステム間で効果的なコミュニケーションをサポートする重要な機能です。
オペレーティングシステムの実装と最適化は、カーネルの構造や設計、システムコールとAPIの効果的な提供、パフォーマンス向上と最適化の手法を含みます。カーネルはOSの中核で、システムコールやAPIはユーザーとOSのやり取りを制御します。最適化は効率的な動作やリソース利用を追求し、高いパフォーマンスを実現します。
ここからは、実在のオペレーティングシステムを題材に、その設計と実装について解説します。
Atlas Supervisorは、Manchester Universityで開発された世界初のバッチ処理オペレーティングシステムの一つです。 Atlasコンピュータ向けに設計され、当初は並列処理やリアルタイム処理をサポートしていました。以下に、Atlas Supervisorが提供していた主な機能について見ていきましょう。
Atlas Supervisorは当時のハードウェアと調和して、計算機利用の合理性を追求した先進的なオペレーティングシステムであり、その後のオペレーティングシステムの発展に影響を与えました。
MIT(マサチューセッツ工科大学)で開発されたCTSSは、1961年に初めて導入されたオペレーティングシステムの一つで、仮想記憶の概念を取り入れていました。これにより、複数のユーザーが同時にメインフレームを使用できるようになりました。
IBM System/360 Model 67は、セグメンテーションとページングを組み合わせた仮想記憶をサポートしたメインフレームコンピュータでした。これにより、大規模な科学計算やビジネスアプリケーション向けに柔軟で効率的なメモリ管理が可能となりました。
MULTICSは、ベル研究所、MIT、およびゼネラル・エレクトリック(GE)が共同で開発したオペレーティングシステムで、セグメンテーションとページングを採用し、仮想記憶の概念を拡張しました。MULTICSのアイディアは後にUNIXに影響を与えました。
MULTICSはUNIXに影響を与えた重要な要因の一つとされています。MULTICSプロジェクトに携わっていたKen Thompson、Dennis Ritchie、そしてその他の開発者たちが、MULTICSの経験から得た教訓を元に新しいオペレーティングシステムであるUNIXを開発しました。
UNIXは、MULTICSよりもシンプルで軽量な設計を持っており、小規模なマシンでも動作可能でした。MULTICSでの経験がUNIXの設計に影響を与え、シェル、ファイルシステム、プロセス管理などの概念が引き継がれました。UNIXは1970年代から広く普及し、その後のオペレーティングシステムの発展に大きな影響を与えました。
UNIXがMULTICSから模倣または影響を受けた具体的な機能や概念はいくつかあります。
以下はその一部です:
これらの機能や概念は、UNIXがMULTICSの経験から学びながら独自のアプローチで実装され、UNIXが普及する一因となりました。
これらの「UNIXらしさ」と言われる特徴は、MULTICSから影響を受けいますが、UNIXの仮想記憶システムは、MULTICSからの直接の影響は受けていません。UNIXの仮想記憶は、主にKen ThompsonとDennis Ritchieによる独自の設計に基づいています。UNIXが登場した時点では、コンピュータの物理メモリが非常に限られていたため、仮想記憶が導入されました。
MULTICSとUNIXの時代背景を考えると、MULTICSの影響は他の要素に比べて相対的に小さかったと言えます。UNIXは小型でリソースが限られたマシン上で動作することを重視し、シンプルで効率的な設計を採用しました。UNIXの仮想記憶の実装は、MULTICSの大規模かつ複雑な設計からは逸脱しています。
UNIXは、ベル研究所で開発されたオペレーティングシステムで、Ken Thompson、Dennis Ritchie、およびチームによって生み出されました。シンプルで柔軟な設計を持ち、C言語で記述されたことが特徴です。UNIXは、マルチユーザー、マルチタスク、およびポータブル性を重視し、その後のオペレーティングシステムの基盤となりました。
OS-9は、Microware Systems Corporationが開発したリアルタイムオペレーティングシステムで、主に組み込みシステムや産業制御などの用途で利用されました。モジューラ設計を採用しています。8ビットプロセッサであるMC6809をターゲットとしましたがOS-9レベルIIでは仮想記憶をサポートしました。
Machは、カーネギーメロン大学で開発されたマイクロカーネルベースのオペレーティングシステムで、仮想メモリやマルチプロセッシングをサポートしています。Machは、そのマイクロカーネルアーキテクチャから派生して、GNU/Hurdなどのプロジェクトに影響を与えました。
OS/2は、IBMとマイクロソフトが共同で開発したオペレーティングシステムで、当初はPC向けに開発されました。マルチタスク、マルチスレッド、およびGUIサポートを提供し、Windowsとの互換性も備えていましたが、後にWindows NTに取って代わられました。
Windows NTは、マイクロソフトが開発した32ビットのオペレーティングシステムで、企業向けに設計されました。安定性、セキュリティ、ネットワーキングの向上を重視し、Windowsファミリーの基盤となりました。 Windows 2000やXPなどのバージョンがこれを基にしています。

本書を読むには予備知識としてアセンブラの知識と、X86系CPUのレジスタなどのアーキテクチャの知識が必要である。
一般的にプログラミングにおいて、ハードウェアの制御は、コンパイラが対応していない命令(特権命令やIO命令など)はアセンブリ言語で記述する事になる。
もし知らなければwikibooks記事『X86アセンブラ』などで解説してある。特に『X86アセンブラ/GASでの文法』『X86アセンブラ/x86アーキテクチャ』『X86アセンブラ/x86アセンブラ』では初心者むけに説明してある。
本書では触れてないが「カーネル」とか「ユーザランド」とかの用語についてはwikibooks『高等学校工業/ソフトウェア技術』などで触れてある。
本書はタイトルが今のところは「オペレーティングシステム」だが、実際はコンピュータアーキテクチャ理論やデジタル回路理論など低レイヤーの理論や実務的知識が混在したものになっている(あとで整理する)。大学の科目の分類が縦割りのタコツボ・時代おくれで、全体像が分かりづらいので、予定では、今後の構成でも、意識的にコンピュータアーキテクチャなど関連分野の説明を、整理後にも、ある程度は残しておく予定。
なのでOSと言うタイトルなのにマイコンCPU(Z80など)にも触れているという状況である。
オペレーティングシステムは、ユーザーがハードウェアレベルのプログラミングをする必要をなくし、パソコンをより使いやすくするために設計されています。UEFIを使用する場合、起動時にUEFIファームウェアがハードウェアを初期化し、UEFIブートローダーを使用してオペレーティングシステムを起動します。デバイスドライバは、オペレーティングシステム開発者が自分で作成する必要があります。
BIOSは古くから使われてきたブートストラップ方式で、起動時にハードウェアを初期化し、オペレーティングシステムを起動していました。しかし、BIOSにはいくつかの制限があり、セキュリティや機能性の向上が必要とされるようになりました。そのため、UEFIが開発され、BIOSからUEFIに移行することが決定されました。UEFIは、より高度なセキュリティ機能、拡張性、性能の向上など、多数の利点を提供しています。現在、多くのPCはUEFIを使用しており、BIOSよりも優れたブートストラップ方式として認められています。
ここでは、歴史的な経緯を紹介するためBIOSにも言及します。
PCに電源を入れた瞬間から順に、以下のようなプロセスが起こります。
UEFI/GPTの場合、PCに電源を入れた瞬間から順に、以下のようなプロセスが起こります。
UEFI/GPTの場合は、UEFIがBIOSよりも高度な機能を提供するため、より高速で信頼性の高いブートプロセスを提供します。 また、GPTはMBRよりも多数のパーティションをサポートし、パーティションのサイズ制限も緩和されます。
BIOSとUEFIは、システムの初期化やブートプロセスに必要なハードウェア情報の取得と管理を行うために、メモリマップを使用します。以下に、BIOSとUEFIのメモリマップの違いを説明します。
UEFIのメモリマップは、より柔軟性があり、より高度な機能を提供します。UEFIは、物理メモリのアドレッシングやデバイスドライバの管理により高度な機能を提供するため、BIOSよりも多数のOSに対応することができます。また、UEFIのメモリマップは、UEFIサービスを介して、システムの状態の取得や変更を行うことができるため、BIOSよりも柔軟性があります。
BIOSとUEFIは、ハードウェアの初期化やオペレーティングシステムの起動など、システムのブートプロセスに必要なタスクを実行するために、割り込みを使用します。以下に、BIOSとUEFIの割り込みの違いを説明します。
U EFIの割り込みは、BIOSと比較して、より高度な機能を提供することができます。UEFIは、C言語で書かれたサービスを呼び出すことによって、より高度な機能を提供できます。これにより、UEFIは、BIOSよりも柔軟性があり、より多数のオペレーティングシステムやアプリケーションに対応することができます。
UEFI(Unified Extensible Firmware Interface)は、従来のBIOS(Basic Input/Output System)に代わる新しいファームウェア規格です。 UEFIは、従来のBIOSに比べていくつかの優れた機能を提供しています。本稿では、UEFIがなぜ必要になり、何が優れているのかについて説明します。
[UEFIとは何か、従来のBIOSとの違いは何かについて簡単に説明します。]
UEFI(Unified Extensible Firmware Interface)は、コンピュータの起動時に実行されるファームウェア規格です。従来のBIOS(Basic Input/Output System)に代わるものとして開発されました。BIOSは、1970年代に開発され、コンピュータの起動時に必要な基本的なハードウェア設定や起動ドライブの選択を行うために使用されていました。しかし、BIOSは時代遅れになり、UEFIが開発されるようになりました。
UEFIは、BIOSに比べて多数の優れた機能を提供しています。セキュリティ機能が強化されており、悪意のあるソフトウェアをブロックすることができます。拡張性が高く、ドライバーやアプリケーションをファームウェアに統合できるため、機能追加が容易になっています。起動時間の短縮や大容量ストレージのサポート、グラフィカルインターフェイスの向上なども特徴的です。
従来のBIOSは、16ビットのアセンブリ言語で開発されており、拡張性が低かったため、UEFIは64ビットのC言語で開発されました。また、UEFIはBIOSよりも起動時間が短く、セキュリティ面でも優れているため、現代のコンピュータに必要な要件を満たすようになっています。
UEFIは、現代のコンピュータにとって欠かせない規格となっています。UEFIの普及により、コンピュータの起動時間が短くなり、セキュリティや機能性も向上するとともに、グラフィカルインターフェイスによってユーザーエクスペリエンスも向上しています。
[UEFIは、セキュリティ機能が強化されています。Secure BootやUEFI Secure Flashなど、UEFIにはセキュリティ機能が多数実装されています。これらの機能は、コンピュータの起動時に悪意のあるソフトウェアをブロックすることができます。]
UEFIのセキュリティ機能の一つであるSecure Bootは、UEFIファームウェアが起動する前に、署名されたオペレーティングシステムやドライバーを認証することができます。これにより、起動時に悪意のあるソフトウェアが起動するのを防止し、システムのセキュリティを高めることができます。Secure Bootは、UEFIの機能の中でも特に重要なセキュリティ機能であり、WindowsやLinuxなどのオペレーティングシステムでもサポートされています。
また、UEFI Secure Flashは、UEFIファームウェアを保護する機能です。これにより、UEFIファームウェアが偽造されたり、マルウェアに感染したりすることを防止することができます。UEFI Secure Flashは、UEFIファームウェアの更新時にも役立ちます。署名されたファームウェアを使用することで、正当な更新プログラムであることを確認し、システムを保護することができます。
さらに、UEFIにはTrusted Platform Module(TPM)と呼ばれるセキュリティチップをサポートすることができます。TPMは、コンピュータに組み込まれた暗号キーを使用し、システムのセキュリティを強化するための機能です。TPMは、セキュリティに特に敏感な企業や政府機関などで広く使用されています。
TPMは、Windows 11 から必須要件となっています。
以上のように、UEFIはセキュリティ面でも優れた機能を提供しています。これらの機能により、悪意のあるソフトウェアからコンピュータを保護し、ユーザーの個人情報や重要なデータを守ることができます。
[UEFIは、BIOSよりも拡張性が高いです。UEFIは、ドライバーやアプリケーションをファームウェアに統合できるため、従来のBIOSよりも機能追加が容易になっています。]
UEFIの拡張性は、従来のBIOSと比べて大幅に向上しています。UEFIは、ドライバーやアプリケーションをファームウェアに統合できるため、従来のBIOSよりも機能追加が容易になっています。これにより、UEFIのファームウェアに新しい機能を追加することができ、コンピュータの機能を拡張することができます。
また、UEFIはファームウェアに対して、エクステンシブル・ファームウェア・インターフェース(EFI)と呼ばれる標準インターフェースを提供します。このインターフェースは、ハードウェアとソフトウェアの間の橋渡しを行う役割を果たし、システムの拡張性を高めることができます。EFIは、ドライバーやアプリケーションが互換性のある方法でインストール、実行されることを保証することができます。
さらに、UEFIは、ファームウェアを実行するプラットフォーム上で動作するアプリケーションのためのフレームワークを提供することができます。これにより、ファームウェア上で直接アプリケーションを実行することができ、オペレーティングシステムが起動する前に必要な処理を行うことができます。UEFIアプリケーションは、UEFIの拡張性を高め、システム管理やユーティリティなどの機能を提供することができます。
以上のように、UEFIは、従来のBIOSよりも拡張性が高く、ドライバーやアプリケーションをファームウェアに統合できるため、機能追加が容易になっています。さらに、UEFIはEFIやUEFIアプリケーションの提供により、システムの拡張性を高めることができます。
アーキテクチャ非依存のドライバとは、特定のCPUアーキテクチャに依存せず、複数のアーキテクチャに対応できるドライバのことです。これは、コンピュータのハードウェアが異なる場合でも、同じドライバを使用できるため、ドライバの開発や管理が簡素化され、互換性の向上につながります。
UEFIにおいても、アーキテクチャ非依存のドライバが使用されています。これらのドライバは、EDK II(EFI Development Kit II)に含まれており、プラットフォームのアーキテクチャに関係なく、UEFI上で動作することができます。
また、UEFIのアーキテクチャ依存のドライバと同様に、アーキテクチャ非依存のドライバもUEFIのドライバモデルに基づいて開発されています。UEFIのドライバモデルは、PEI(Pre-EFI Initialization)フェーズ、DXE(Driver Execution Environment)フェーズ、BDS(Boot Device Selection)フェーズ、そしてランタイムフェーズの4つのフェーズに分かれており、それぞれのフェーズで必要なドライバがロードされます。アーキテクチャ非依存のドライバは、これらのフェーズのうち、DXEフェーズでロードされます。
[UEFIは、BIOSよりも高速な起動時間を実現します。UEFIは、従来のBIOSに比べて、メモリの管理が効率的であるため、起動時間を短縮できます。]
UEFIは、BIOSよりも高速な起動時間を実現することができます。これは、UEFIが従来のBIOSに比べて、メモリの管理が効率的であるためです。UEFIは、BIOSと比べて、より多くのメモリをサポートすることができます。また、UEFIは、メモリ管理において、従来のBIOSよりも優れたアルゴリズムを採用しています。これにより、起動時のメモリ管理がより効率的に行われ、起動時間が短縮されます。
さらに、UEFIは、ブートローダーの読み込みにおいても優れた性能を発揮します。UEFIは、従来のBIOSよりも大きなディスク容量をサポートしており、UEFIファームウェアによるブートローダーの読み込み速度が速いため、起動時間を短縮することができます。
また、UEFIは、ファームウェアにおいて、並列処理を採用することができます。これにより、UEFIは、複数のタスクを同時に処理することができます。従来のBIOSでは、タスクを順次処理する必要がありましたが、UEFIでは、並列処理により、タスクを同時に処理することができるため、起動時間が短縮されます。
以上のように、UEFIは、BIOSよりも高速な起動時間を実現することができます。これは、UEFIがメモリの管理が効率的であり、ブートローダーの読み込みにおいても優れた性能を発揮するためです。さらに、UEFIは、並列処理を採用することができるため、起動時間を短縮することができます。
[UEFIは、BIOSよりも大容量ストレージのサポートが容易です。UEFIは、GUID Partition Table(GPT)をサポートしているため、2TB以上のHDDやSSDなどの大容量ストレージをサポートすることができます。]
UEFIは、BIOSよりも大容量ストレージのサポートが容易です。UEFIは、GUID Partition Table(GPT)をサポートしており、2TB以上のHDDやSSDなどの大容量ストレージをサポートすることができます。
従来のBIOSでは、Master Boot Record(MBR)を使用していたため、2TB以上のストレージをサポートすることができませんでした。MBRは、512バイトのサイズ制限があるため、2TB以上のストレージを使用する場合には、パーティションを複数に分割する必要がありました。しかし、UEFIは、GPTをサポートしているため、2TB以上のストレージを単一のパーティションとして認識することができます。これにより、大容量ストレージの管理が容易になり、ストレージの最適化がより簡単になります。
また、UEFIは、多くのストレージデバイスに対して、より高度な機能をサポートすることができます。UEFIは、Advanced Host Controller Interface(AHCI)やNVMeなどの高度なストレージインターフェースをサポートしており、SSDなどの高速なストレージデバイスの性能を最大限に引き出すことができます。
以上のように、UEFIは、BIOSよりも大容量ストレージのサポートが容易であるため、2TB以上のHDDやSSDなどの大容量ストレージをサポートすることができます。また、UEFIは、高度なストレージインターフェースをサポートすることができるため、ストレージデバイスの性能を最大限に引き出すことができます。
[UEFIには、BIOSよりも優れたグラフィカルインターフェイスがあります。これにより、BIOSよりも視覚的な操作が可能になり、ユーザーエクスペリエンスが向上します。]
UEFIには、BIOSよりも優れたグラフィカルインターフェイスがあります。従来のBIOSは、文字ベースのインターフェイスを使用しており、操作が限定されていました。しかし、UEFIは、グラフィカルインターフェイスを使用することができます。これにより、視覚的な操作が可能になり、ユーザーエクスペリエンスが向上します。
UEFIのグラフィカルインターフェイスは、通常、マウスやタッチパッドを使用して操作することができます。また、キーボードでも操作することができます。グラフィカルインターフェイスを使用することで、UEFIの設定や構成をより簡単に行うことができます。また、グラフィカルインターフェイスを使用することで、BIOSよりも視覚的な情報を提供することができます。
さらに、UEFIのグラフィカルインターフェイスは、スクリーンリーダーや拡張性の高いアシスト技術をサポートすることができます。これにより、視覚障害者などのユーザーでも、UEFIを使用することができます。
以上のように、UEFIには、BIOSよりも優れたグラフィカルインターフェイスがあります。グラフィカルインターフェイスを使用することで、UEFIの設定や構成をより簡単に行うことができ、視覚的な情報を提供することができます。また、スクリーンリーダーや拡張性の高いアシスト技術をサポートすることができるため、視覚障害者などのユーザーでもUEFIを使用することができます。
UEFI Shellとは、UEFIベースのシステムにおいて、UEFIファームウェアが提供するコマンドラインシェルのことです。UEFI Shellは、UEFIファームウェアによって提供される機能の一部であり、ユーザーがUEFIファームウェアを直接制御することができます。
UEFI Shellは、UEFIファームウェアによって提供されるため、UEFIベースのシステムにはすべて搭載されています。UEFI Shellは、UEFIの拡張性の高さを示す代表的な例であり、ドライバーやアプリケーションをシェルに統合することができます。また、UEFI Shellは、UEFIのセキュリティ機能を使用してシステムのセキュリティを確保することもできます。
UEFI Shellは、UEFIベースのシステムの設定やデバッグ、トラブルシューティングなどに使用されます。ユーザーは、UEFI Shellを使用して、ストレージデバイスの操作、ファイルシステムの操作、ネットワークの設定などを行うことができます。UEFI Shellは、コマンドラインインターフェースを提供するため、一般的なコマンドやスクリプトを使用してタスクを自動化することもできます。
UEFI Shellは、UEFIの機能を理解するために必要な知識を持つエンジニアや開発者にとって、非常に便利なツールです。UEFI Shellを使用することで、システムの動作やパフォーマンスを最適化することができます。
UEFIは、ネットワークブートに必要なプロトコルとサービスを提供することにより、ネットワークサポートを強化しています。UEFIネットワークスタックには、以下のようなプロトコルやサービスが含まれています。
UEFIのネットワークサポートは、OSに依存しないため、UEFI自体がネットワーク接続を確立でき、ネットワーク上のリソースにアクセスできることを意味しています。これにより、ネットワーク上のリモートサーバーからOSをブートすることができます。
UEFIは、複数のファイルシステムをサポートしており、主要なものとしてFAT32、NTFS、ISO 9660、UDFなどがあります。UEFIファームウェアには、これらのファイルシステムにアクセスするためのドライバーが組み込まれています。
特に、UEFIはFAT32ファイルシステムを必須としており、UEFIシステムパーティションにはFAT32が使用されます。このパーティションには、UEFIファームウェアや起動ローダー、設定ファイルなどが含まれています。UEFIファームウェアは、このパーティションを自動的に認識し、必要なファイルを読み込んでシステムを起動することができます。
また、UEFIはネットワークファイルシステムにも対応しており、PXEブートなどを利用して、ネットワーク上のサーバーからOSを起動することができます。これにより、大規模なサーバー環境でのOSのデプロイやメンテナンスが容易になります。
Macintoshは、UEFIの前身であるEFI(Extensible Firmware Interface)を採用しています。EFIは、Appleが1998年に開発したプラットフォームファームウェアで、Intelと共同で開発されました。当初はMacintoshのプラットフォームファームウェアとして採用され、後にUEFIの基礎となりました。
MacintoshのEFIは、標準的なPCのUEFIとは異なり、Macintosh固有の仕様があります。たとえば、MacintoshのEFIは、GPTスキームに基づくブートデバイスの選択方法が異なります。また、MacintoshのEFIには、OS XやmacOSの起動時に使用されるBoot Campなどの固有の機能が含まれています。
Macintoshは、UEFIを正式にサポートするようになったのは、2015年以降のMacモデルからとなりました。これにより、Macintoshも他のPCと同様に、UEFIの機能やセキュリティを活用することができるようになりました。
IBMは、UEFIの策定において重要な役割を果たしました。IBMは、PC BIOSの標準化を提唱しており、1990年代初頭にPC/AT互換機向けのPC BIOS規格を開発しました。しかし、PC BIOSは、コンピュータの機能拡張に対応できなくなってきたことから、IBMは新しいファームウェア規格の策定を提唱しました。
その結果、UEFIが開発され、2005年にはUEFIフォーラムが設立されました。IBMはUEFIフォーラムの設立メンバーの1つであり、UEFIの開発において重要な役割を果たしました。また、IBMは、x86_64アーキテクチャにおけるUEFIの開発にも取り組んでいます。
PowerPCは、UEFIのサポートを提供するアーキテクチャの1つです。UEFIは、x86アーキテクチャだけでなく、ARMやPowerPCなどの様々なアーキテクチャをサポートしています。PowerPCにおいても、UEFIを使用することで、従来のBIOSよりも高速な起動時間や、セキュリティ機能の強化などの恩恵を受けることができます。また、UEFIを使用することで、PowerPCに対するソフトウェア開発の容易化も期待されています。

Power Architectureは、サーバーや組み込みシステムで使用されるプロセッサアーキテクチャのファミリーで、PowerPCプロセッサを含んでいます。UEFIは、x86アーキテクチャに最初に導入されたが、その後、他のアーキテクチャにも広がっていきました。Power Architectureに対するUEFIのサポートは、2012年にUEFI Forumによって標準化されました。
Power Architectureにおいて、UEFIを実装する際には、EFIファームウェアをPowerPCアーキテクチャ向けに移植する必要があります。Power Architecture向けのUEFIの実装には、オープンソースのOpenPOWER FoundationとIBMのOpen Firmwareが利用されています。また、UEFIの実装には、Linuxカーネルのようなオープンソースソフトウェアも使用されています。
[UEFIは、BIOSに比べてセキュリティや拡張性、起動時間の短縮など多数の優れた機能を提供しています。今後も、UEFIはコンピュータ業界で重要な役割を果たすことが予想されます。]
以上のように、UEFIはBIOSよりも優れた機能を持ち、より高度なセキュリティや拡張性、起動時間の短縮などの面で優れています。さらに、UEFIは大容量ストレージのサポートや、より視覚的なグラフィカルインターフェイスも提供しています。これらの機能は、現代の高度なコンピュータシステムにとって必要不可欠であり、UEFIはその要件を満たすために開発されました。 UEFIがBIOSを置き換えることにより、コンピュータ業界はますます効率的になり、コンピュータの起動時間が短縮され、より高度なセキュリティ機能を持つことができます。UEFIは、現在のコンピュータシステムの基盤となっており、今後もますます重要な役割を果たすことが予想されます。
一般に、エミューレーターを使って実験します。
無料のフリーソフトでも、w:QEMUなどのエミュレータを使えるので、アセンブラのw:NASM(無料ソフト)などで(アセンブリコード入力を経由して)機械語を入力していきます。
※ 次の節で、qemuの使い方を大まかに説明する。
原理的には、アセンブラを経由せずともバイナリエディタ(Hex editor)といわれるもので機械語を直接に書いてプログラムするのも、原理的には可能です。そもそもアセンブラ自体どうやって開発されたかを想像すれば、おそらくバイナリエディタを、流通しているCPU用のアセンブラ命令にあわせて特化してプログラミング用に作られたソフトウェア(がアセンブラ)なのでしょう。
原理の理解としてはバイナリエディタの存在に気づくことも重要ですが、しかしバイナリエディタによるプログラミングではコード記述が覚えづらく非現実的ですし、一目では内容が分かりづらいです。なので、一般にアセンブラで入力していくのが、OS製作では現実的でしょう。
さて、NASMは、あくまでエミュレーター上での仮想化なので、完全には仕組みを再現していませんが、しかし個人の学習では、止む(やむ)を得ません。 もしもエミュレータを使わずに、BIOSのブート設定を 毎回 書き換えて実機のパソコンでブートの実験などを毎回したとしたら、とてもメンドウです。なので、エミュレータを使って、手間を省きます。
なお、一般的なLinuxなどでは、リリース直前の開発の後半などで、確認のために最終的に開発者たちはDVDなどに書き込んでブートしてみたりするなどして、実験します(いわゆる「ベータテスト」などで、DVDのISOを無料配布している)。
なお余談ですが、OS自作でなくCPU自作をしたい場合、w:GNU Binutilsなどの無料のクロスアセンブラがありますので、そういうのを活用します。半導体製造工場などを個人は持ってないので、ソフト的にエミュレートするしかありません。
エミュレータにも種類や方式が色々とあります。
方式のひとつとして、ホストOSのインストールされているパソコンのCPUを間借りする方式があります。
別の方式として、ホスト側のCPUは間借りせずソフトウェア内に仮想のCPUを制作する方式のものなどもあります。
コミュニティ ベース の qemuやBochs というエミュレータは、(ホストCPUをあまり)間借りしないで、ソフト的に仮想のCPUを作る方式のものである
いっぽう、オラクル社のVirtual Box やヴイエムウェア社のVMWare というエミュレータは、ホストPCのCPUを間借りする方式のものである。
私たちの学習の目的には、VirtualBox 的に複数のCPUが混ざり合うと学習的に分かりづらくなるので、qemuのようなソフト的にCPUの機能を再現するほうが分かりやすいと考え、qemuを優先して紹介することにする。
また、qemu はオープンソースである。
なお、Virtual Box は昔は非オープンソースだったが、現在はオープンソース版のVirtual Box がある。
なお、オラクル社のようなホストCPUを間借りする方式にも長所はあり、仮想化の中では処理速度を速くしやすいという長所もある。

本書では、とりあえず qemu を前提として説明する。

前提として、ダウンロードとインストールは既に終わっていると仮定する。
qemuは、コマンドプロンプトから使うソフトウェアである。
なので、まずパス(Path)を通さないといけない。
具体的には、環境変数PATHにqemuのあるフォルダーを追加する。
さて、パスが追加し終わったら、はたして本当にパスが通ってるの確認のため、Windwosのコマンド プロンプトを起動して
というふうに、コマンド「qemu-img」でも入力してみよう。
なお、「qemu」というコマンドは無いので、「qemu」とコマンド入力しても。
さて qemu-imgコマンドは引数を指定して使用するコマンドなので、上記コマンド「qemu-img」を実行すれば、端末上で引数が足りないことをqemuから警告されるハズである。
のように、qemuから警告されれば、ひとまずqemuのインストールは成功である。
あらかじめ、起動したいファイルを機械語で作っておき、たとえば、testos.img とかの名前をつけて、ホームディレクトリーなどコマンド端末の認識できる場所に保存しておく。
そしてコマンド端末で
のようにコマンド qemu-system-i386 を使えばいい。

では、そのOSイメージをどうやって作るか。
原理は、
まず、アセンブラの nasm で書く。(nasm は Netwide Assembler ともいう。)
あらかじめアセンブラコード形式で、OSにしたいファイルをアセンブリコードで作っておいて、testos.asm などの名前で保存しておく必要がある。
nasmなら、testos.asm を(PEフォーマットでない、直訳の)機械語にするコマンドは
だけで終わる。
そして、こうして作成したブートイメージをqemuで起動する方法は、ホームファイルに先ほど作成した testos.img を置いた上で、
のコマンドだけで終わる。
では、元になるアセンブリコードをどうやって調達するか? とりあえず、ネットで読者の勉強用に(彼らの)自作OSのブートローダーのアセンブリコードを公開してくれている人がチラホラといるので、彼らのコードで実験するのが良いだろう。
ネット上に Bochs の使い方の入門書がぜんぜん無いので、wikibook で教えることにする。
Bochsでは、アセンブルはできない。
Bochsの用途は、すでに別ツール(たとえば nasm んど)で作成ずみの img ファイルをBochsで起動するだけのものである。
asmファイルからimgファイルへのアセンブルは、あらかじめ nasm などで行っておく。
Bochsの利便性は、コマンドを覚えなくて言いことである。起動コマンドのBochsだけ押せばウィンドウが起動するので、あとはそのウィンドウ側でGUI的に操作してエミュレータの起動をできるという便利ツールなエミュレータが Bochs である。
さて、Boshs をインストールしてから、環境変数(パス)の設定を終えれば、 コマンドプロンプトで、コマンド bochs で起動する。つまり
のように「bochs」の部分を入力する。
で、起動するとダイアログ画面が現れる。
さて、これだけでは、そもそも何のファイルを起動するかすらも設定されてない。なので、これから、このダイアログ画面で、それを設定する。
まず、真ん中の項目(中央ペイン)にある「Edit Option」 の「Disk & boot」をダブルクリックすると、画面が遷移して、
オプション画面である「Bochs Disk Options」画面になる。この項目で、何のファイルを起動するかを設定できる。 そのためには、
その「Bochs Disk Options」画面の中で、タブ「ATA Channel 0」> 子タブ「First HD/CD on Channel 0」 をクリックすると出てくる画面に、
上から2段目あたりに
という項目があるので、その のなかに、起動したいimgファイル名を入れる。
たとえば、「testos.img」ファイルを起動したいなら、
のようになる。
こうして、(おそらく)あとはこのオプション画面を終了して(ダイアログ・ウィンドウの右上のクローズ用の×ボタンを押せばいい)メインメニュー画面に戻り、右ペインにあるStartボタンを押せばいい。
Panic ウィンドウが表示されて Message 欄に「specified geometry doesn't fit on disk」 と書いてあるが、このまま左下の欄にある Continue をクリックして、OKを押せばいい。
なお、Path or physical device name の設定をしてないで空欄のままにしておくと、Startの際に Message 欄に 「no bootable device」 と出る。
BochsのDtart>OKの後のエミュレート起動後の終了方法は、ウインドウの上部にあるメニューバーの右側のほうに、○印の中にタテ線「|」のある終了ボタン「(|)」があるので、これを押せばいい。(右上の×ボタンは使えない。×ボタンをクリックしても反応しない。)
一般的なアセンブラには DB 命令というのがあり、これはORG命令などで指定したメモリに値を書き込む命令であるが、これで機械語も書き込みできる。
もし、機械語を書き込む先を、メモリではなく、ハードディスクやUSBメモリやフロッピーディスクなどのブート可能なメディアにすれば、原理的には、これでブートローダを作れる。
なお、DB とは data byte の略だと言われている。

さて、では、OSを作るためには、まず、ブートセクタを書き込めばいいのですが、では、どういう内容のことを書き込めばいいのでしょうか。

まず、ブートローダを書き込むわけですが、
規格により、
また、
と決まっている。

なので、このために
をどこかに書き込む必要があります。
times とは、nasm の擬似命令のひとつで、繰り返し命令のことです。
「aa55」の署名を書き込まずにQemu上で機械語を起動しても、いくつかのメッセージのあとに「No Bootable device」などと表示されるだけです。
さて、上記コードの場合は
という命令です。
$ は、そのtimes命令が出されたときの現在のアドレスです。
$$ は、現在のセクションの最初のアドレスです。
間違えて、「55aa」(マチガイ!)を書き込まないようにしてください。詳しくは『w:リトルエンディアン』で調べてください。
MBRのサイズは512バイトと決まっており、その末尾2バイトに「aa55」と書き込むので、times 繰り返し命令 では「510」と2バイトぶん、余らせています。

なので、とりあえず下記のように書き込みましょう。

成功すれば、 qemu上で、「H」と表示されます。
int 0x10 とは、ディスプレイへの割り込み命令のひとつです。int でBIOSにより割り込み命令を指示しています。intの引数で、どんな割り込みをするかを指示しており、int 0x10 はビデオサービスの割り込みです。
ですが、int 0x10 だけでは、ディスプレイに文字も画像も表示できません。
int 0x10に加えて、さらに、何を割り込ませるかの指定を行う必要があり、 mov ah, 0x0e で文字割り込みを指定しています。(※ 詳しくは『en:w:INT 10H』(英語版ウィキペディア)などを参照してください。)
int 0x10 の命令は、上記の手本コードのように、 アキュムレータ レジスタで指示しなければなりません。
なお、学校などで、もしかしたら、情報科学・計算機科学の教育では「アキュムレータ」とは「加減乗除などのためのレジスタ」とか習うかもしれませんが、しかし実際のCPUでは、(上記コード例のように)設定などの一時保存にもアキュムレータが流用されています。

。CR0の最下位ビットのが0ならリアルモードです。実際には、プロテクトモードの移行のためには、さらに グローバル デスクリプタ テーブル(GDT)というものを作成する必要があるが、初学者には当面は知らなくていいので、もうプロテウトモードの説明は後回しにする(※ 現時点では未記述)。

さて、この場合での int 0x10 は、alレジスタにある文字を表示できます。
「Hello 」と表示させたければ、「H」だけでなく、同様の操作を繰り返し、「e」「l」「l」「o」を追加で表示させればいいだけなので、

でブート後に「Hello」と表示できます。
ラベルやジャンプ命令を使うと、繰り返し命令を実装できる。
上の「Hello」のプログラムを、ラベルなどによる繰り返し処理でプログラムするとするなら、下記のようになるl

org 0x7c00 とは、このようなテキスト処理をするのにBIOSに予約されている領域が7c00 なので、そこから書き始める必要がある。 org は擬似命令であり、これからの書き込みのメモリ位置を指定する命令である。
また、 mov si, greet は形式的には、あたかもラベルgreetの内容を代入するかのような表現ですが、
実際は、単にgreet ラベルの先頭のメモリを代入するだけです。
int 0x10 は、繰返し命令の中で、毎回、使用する必要がある。
次のようlodsb 命令とsiレジスタを使っても良い。lodsbは[ds:si]からalに1バイト読込む(ロード load)命令であり、さらに使用後に自動的にsiレジスタの指し示す位置を1バイトぶんだけインクリメント(DF=0の場合、DF=1の場合はデクリメント)してくれるので、手間が省ける。
※ 2022年現在のx86プロセッサは、x86命令セットをマイクロ命令に分解し、マイクロ命令の組合せによっては複合的なマイクロ命令に合成し実行しています。ところが、lodsbの様な複雑な命令はマイクロ命令に翻訳されず固定的なマイクロコードにより実行されます。このため、かつて命令読込みのオーバーヘッドがなくせるという理由で「最適化」の代表だった複雑な命令を使うことは、現在は実行速度を低下させることになります。
解説
lodsb の内容は
と等価である(DF=0の場合、また OF, SF, ZF, AF, PF は inc si の結果により修飾されるので厳密には異なる)。

の代わりに
でも良い。
また test の代わりに
でも良い。
※ かつては、定数とレジスタの比較はレジスタファイルの読出しハザードがありストールの原因になりましたが、Sandy Bridgeマイクロアーキテクチャ以降のプロセッサであればマイクロフュージョンの対象となり、test命令やor命令を使うメリットはなく、專ら可読性を損なうだけです。
まず、設定として、
というコードが必要です。
int 0x10 はグラフィック割り込みです。
グラフィック割り込みの場合、ahは0に固定するように規格で決まっています。
alによって、ビデオモードを指定しています。
alが0x13なら、320 x 200ドット x 16色モード(8bitカラー)
という意味です。
一般的なパソコンではグラフィック描画用のVRAMアドレスは 0xa0000 から 0xaffffの領域が割り当てられています。
なので、mov命令で、このアドレスに書き込むと、VRAMに直書きできます。

で、問題は、通常の16ビットCPUモードのmov命令では、 メモリ番号は4ケタまでしかアクセスできない。「a0000」や「affff」は5ケタであることに注目。
で、しかも、ブートローダ起動中のリアルモードでは、この16ビットCPUのモードである(らしい)
なので、ともかく、通常の方法ではアクセスできない。
5ケタのメモリ番号にアクセスするためには、
「セグメント方式」という手法を使う。
簡単に言うと、
なお、上式の数は十進数である。
16進数になおすと、単にセグメントベース×16は、末尾に0をつけたすだけである。
これらのセグメントベースを格納するために「セグメントレジスタ」と言う専用のレジスタを使う。
16ビットCPUのセグメントレジスタには
の4つがある。
なおFSとGSは32ビットCPU以降のセグメントレジスタである。
CSは、CPUが実行するプログラムを格納するためのセグメントとして、CPUに使用させる。
DSは、メモリの読み書きといったデータ関係の手段のセグメントとして、CPUに使用させる。
SSはスタック関係のセグメントで使用。
「エクストラ」とは、「その他」とか「追加の」のような意味。
よく分からなければ「セグメントベース」でググると、詳しく紹介してくれている親切なITブロガー日本人さんが何人かネットにいるので、それらのページを参照してください。
で、書式は、[セグメントベース:オフセット]の書式である。

下記のようなコードで、ブート直後の画面の任意の場所にピクセル単位で色をぬれる。

さて、mov 命令でメモリに書き込むには mov BYTE [123], 0x4567 の書式で書き込みます。
この場合、メモリの123番地に、数値(16進数で)4567を書き込むわけです。
[ ] をつけると、メモリへの指示だとアセンブラなどが認識します。
ここでの「BYTE」 は1バイト長で書き込め、という命令です。
2バイト長なら(BYTE でなく) WORD になります。
4バイト長さなら DWORD になります。
なお
のように書いた場合は、メモリ123番地にある内容をレジスタalに書き込め、という命令になります。
このように、(書き込みだけでなく)メモリからの読み込みにも [ ] は使えます。
つまり、 [ ] をつけると、メモリへの指示だとアセンブラなどが認識します。

0xa0000は画面の左上だが、そこに色を塗っても見づらいので、
上記コードでは 0xa1111 から色を塗り始めることにした。
なお 0xaffff は画面の右下である。
画面の真ん中の上のほうに、なんか緑色っぽい線が5ミリくらいの長さで水平に引かれている結果が表示されると思う。
上記のコードでは、分かりやすさを重視して、あえて繰り返し命令(ラベルやジャンプ命令で実装できる)を使わなかったが、実際に図形を書きたい場合は、ラベルを活用して繰り返し命令で処理するのが効率的だろう。
なお、上記コード例1で
とあるが、
これを、(下記はエラーになる)
とまとめても、なぜかエラーになる。
なので、手本のコードのように、レジスタを経由する必要がある。
一般的名パソコンでは、英数字などのテキスト出力も、ブート直後の段階では、専用のメモリ領域が用意されるので、このメモリを書き換えることで、テキスト文字を出力できます。(ただし、漢字や平仮名・カタカナなどは無理。)
ブートプログラムの確認作業などに、便利な機能でしょう。

また、 int 命令によって、1文字ずつ書いていく方法は、原理は単純ですが、実装では、処理速度があまり速くないという問題があります。
ラベル命令やジャンプ命令などを使ってコードの行数を減らしても、int命令で書き込みをするよりも、テキスト用メモリを直に書き換えするほうが処理速度が速くなります。
一般的なパソコンでは 0xb800 からの領域がテキスト用メモリに割り当てられています。
なので、まず
と指定します。


メモリ領域のうち、0xa0000 から0xfffff までの領域は通常のパソコンでは、BIOSがハードウェアを管理するために使用することになっています。
このため、それらハード管理以外のソフトウェア的な処理をするためにメモリ使用したい場合は、この領域を避けてメモリを使用する必要があります。
こういった用途には Linuxなどの現代のオープンソースOSでは、 メモリを使用する際には 0x100000 以降の領域を使うのが一般的です。
原理的には 0xa0000 未満の領域も使用可能ですが、使いすぎ等のミスによって0xa0000以降にハミ出る恐れがあるので、なるべく 0x100000 以降の領域だけを使うほうが安全でしょう。

まず、フロッピーディスクやハードディスクなどに読み書きのできる割り込み命令で、 int 0x13 というのがあります。
2000年代の現代では、これを大容量デバイス用に拡張した拡張 int 0x13 というのがあります。
どちらの int 0x13 とも、ドライブ番号は、DLレジスタ(データレジスタの下位(Low)の部分)で指定します。
このように、int 0x13 では、割り込み時におけるレジスタの役割が決められています。
さて、拡張 int 0x13 を使って、フロッピーディスクやハードディスクなどの記憶媒体に割り込みをできます。(USB対応については今後の見通しは不明。書き込みできるものもあるようだが、)
int 13h は、レジスタなどの数値で 書き込みの方式や対象を指定します。拡張 int 13h と、非格調 int 13h では、レジスタの解釈が違っております。
格調 int 0x13 のほうの方式を LBA方式といいます。
格調 int 0x13 では、レジスタに収まりきらない様々な情報を、任にの Disk Addres Packet (DAP)という場所に配置しており、その形式も決まっています。
※ 調査中

なお、拡張されてない int 0x13 は、ハードディスク容量などの小さい時代の古い規格のものであり、現代では、読み書きに時間が掛かったり、あるいは不可能です。
非拡張の int 0x13 では、ドライブ番号は、DLレジスタ(データレジスタの下位(Low)の部分)で指定します。
AHレジスタが 0x02 なら 読込み、AHレジスタが 0x03 なら 書込み です。

int 13hともいう。
まず、int命令で、キーボードサービスの割りこみがあり、 int 0x16 がキーボードサービスである。
さてint 0x16 を呼び出す際、
なら、ah = 0x00 は、キーボードのキー入力待ちである。ah = 0x10 だと拡張キーボード対応らしい 。

実は一般的なパソコンでは、アセンブリ言語の命令で、いくつかのハードウェアに読み書きのアクセスするための、専用の命令がある。
IN 命令と、 OUT命令である。
そして、キーボードなど、昔のどこのパソコンにも存在したハードウェアは、実はハードウェア番号が決められている(「I/Oポートアドレス」などという)。
たとえば、キーボードはハードウェア番号(I/Oポートアドレス)が十六進数で0x60 番である。(メモリマップとは別。メモリアドレスの0x0060などにdb命令で書き込んでも、目的のハードにはアクセスできない。 )
IN命令で、引数で指定したレジスタ(普通はALレジスタやAXレジスタを指定する)に、もうひとつの引数で指定した目的デバイスから送られた値が保存されます。(引数の順序はアセンブラの種類などによって異なるので、説明を除外。)
また、OUT命令で、引数で指定したレジスタ(普通はALレジスタやAXレジスタを指定する)に格納されている値が、もうひとつの引数で目的デバイスに送られます。

たとえば、下記のようなコードで、キーボードの文字「E」または前後のWかRを押すと、1行ぶん下の位置に文字「G」が表示される。

in al, 0x60 を使えば、キーボードコントローラーから送られてきたキーも al に入力されます。
in al, 0x60 とは、けっして「レジスタ al に 60 を代入しろ!」(×、マチガイ)という意味ではなく(そもそも定数の代入だけなら mov 命令だけで可能である)、「ポートアドレス 0x60番 のポートから送信されてきたデータを、レジスタalに代入しろ」という意味です。勘違いしないでください。
そして、キーボードコントローラーのポートアドレスが 0x60 なので、めでたく、上記コードでキーボードから押されたボタンの情報を受け取れます。
さて、キーボードを押したとき、押したボタンに対応するスキャンコードが、パソコン内部にあるキーボードコントローラー(KBC)という装置に送信される仕組みになっています。
このスキャンコードは、アスキーコードとは異なります。
上記コード例にある cmp al, 18 の数値「18」とは、スキャンコードでの番号です。だいたいスキャンコードで18番のあたりが文字 W,E,R のどれかのあたりです。
また、アスキーでは「2」と「"」とは別の文字ですが、しかし日本語キーボードの場合、「2」と「"」はボタンが同じなので、スキャンコードでは同一になります。
このように、物理的に同じ位置にあるかどうかで、スキャンコードは決まります(なお、JIS配列キーボードやUS配列キーボードのように言語が違うキーボードでも、位置が同じなら、ほとんどのボタンのスキャンコードの内容も同じ場合が多い)。

また、スキャンコードは、押されている時に送信されるコード(「メイク コード」という)と、離した瞬間に送信されるコード(「ブレイク コード」という)とが、それぞれ違うコードです。
日本語キーボードは OADG という規格にほぼ統一されています。
ですが、世界的にスキャンコードの規格は、古いものでも3種類くらいあり、さらにUSBキーボードの規格は別です。このため、日本語 OADG のスキャンコードも、現在でも3種類くらいあります。
下記のリンクが詳しいです。
なお、一般的に「メイクコード/ブレイクコード」の書式です。
たとえば、ボタン「1」(「!」と同じ)のスキャンコードが「16 / F0 16」とかかれていれば、メイクコードが「16」であり、ブレイクコードが「FD 16」という意味です。(ある規格では、ブレイクコードは、メイクコードの先頭にF0がついたものになっている。)
なお、キーボードコントローラーもインテルなどが製造していました。かつて Intel i8042 というキーボードコントローラーが有名でした。
より正確な仕組みとしては、シリアル通信(PS 2 信号)などで送られた信号をKBCあたりでスキャンコードに変換しているわけですが、CPUから見ればスキャンコードしか見えないので、あまり気にしなくていいでしょう。

0から作るOS開発 カーネルローダその3 プロテクティッドモードへの移行とA20
※ 本wikibooksの当ページが完成するまでの間、上記の参考サイトが分かりやすくて役立つと思いますので勉強してください。


次のようなコードを使えばledが点滅するらしいのだが、しかしエミュレータでの実験では分からなかった(Windows起動により、すでにLEDが点灯しているので、区別しづらい)。
0xED というのは、LED点灯に関する命令の番号。

一般的なブートローダのいくつかには、ブートセクタに BIOS Parameter Block (BPB)というものが書かれており、これは BIOS への指示や設定を出すブロックです。
パソコンの電源を投入して、まず最初に起動するのは BIOS なのですから、このブロックが必要です。少なくとも Windows系OSのブートローダーでは、そうなっていると言われています。
このように、ブートセクタは、書式がほぼ決まっています。
という構成になっています。
ジャンプしてしまうので、BPB はプログラムカウンタでは読み取れず、BPBはBIOSしか読み取れないことに注目しましょう。
市販のOS自作本にある、DB命令で書き込む冒頭の 「DB 0xeb」 と言うのも、このJMP命令のことです。
JMP命令はX86系CPUの機械語では eb です。
『IA-32 インテル®アーキテクチャソフトウェア・デベロッパーズ・マニュアル』『中巻A:命令セット・リファレンスA-M』3-411


プロテクトモードとは、CPUの32ビットモード(および32ビット以上のモード)のこと。
32ビットモードには、アクセス権の無い状態からのアクセスを禁止するという、特権レベルによる保護機能などがあるので、プロテクトモードという。
プロテクトモードにいこうするためのコードの一部を抜粋すると、おおむね書きのような感じになる。
cr0レジスタの最下位ビット(PEビットという)が1だとプロテクトモードである、という仕様である。
の3行の処理で、cr0レジスタの最下位ビットを1に設定している。なお、このcr0の最下位ビットのことをプロテクト・エネーブルド pr0tect enebled という意味でPEビットという。
設定後にわざわざジャンプ命令 jmp を通す理由は、CPUの先読みした命令を破棄するためである 。ジャンプ命令には、先読みを破棄する機能がある。
なお、パイプラインという仕組みにより、CPUは先読みしている。このパイプラインの先読みを破棄することをフラッシュという。
実はCPUは、いくつか先の命令をすでに先読みしている(これがパイプライン)。プロテクトモード以降では、それが不具合の原因になるので、いったんフラッシュする(カラにする)必要がある。
なので、CPUのパイプラインをフラッシュするためにジャンプ命令を使っている。


さて、一般にWindowsやLinuxなどのOSには、パーティションという、インストール時にハードディスクの使用領域を決める機能がある。
実はCPU側に、メモリに関する機能だが、似たような動作を機能がある。
GDT(グローバル ディスクリプタ テーブル Global Descriptor Table)といって、メモリのアドレスのどこからどこまでがそのCPUで使える領域を定義する機能がある。
で、プロテクトモードでは、あらかじめ、このGDTを設定しないと動作しない。そういう仕様で、むかしのインテルあたりの人が決めてしまったので、従うしかない。
で、実はCPUにGDTレジスタ(GDTR)という、GDTの場所を保管する専用レジスタがあるので、このGDTレジスタにGDTのアドレスなどの情報を入れる必要がある。
さらに、このGDTレジスタに書き込むための専用の命令 lgdt (ロードgdt)があるので、これを使う必要がある。(「書き込みだから save では?」という疑問も、わくかもしれないが、こういう名前に決まってしまってるので、従うしかない。)

同様に IDT(Interrupt Descriptor Table)というのがある。
さらに、16ビット時代の昔はCPUのアドレスバスが20本までだったので、リアルモードでは利用するアドレスバスが20本までという制限が掛かっており、A00からA19までを使用している。A20以降はマスクされている。
この制限のことを「A20のマスク」という。プロテクトモードに以降するためには、このA20のマスクを解除しないといけない。
下記の順序で作業しないといけない。そういう仕様である。
A20マスクの解除には複数の方法がある。
キーボードコントローラから解除できる理由は、単に昔のインテルかどこかの人が設計したとき、たまたまキーボードコントローラ用のアドレスバスが余ってたからだけと言う理由らしく、あまり技術的な深い意味は無い。
なお作業の順番について、A20のマスク解除の順場は多少前倒しになっても平気なようである。
ある程度、理解が進むと、 Linux など実際に活用されているオープンソースOSのブートローダを調べたいと感じるかもしれません。
まずOSの起動で最初に動くのはブートローダだからです。
しかし Linux を開発している kernel.org のサイトには、ブートローダは無いのです。
Linux で仕様されているブートローダは、Gnu(グニュー)というオープンソース・ソフトウェア団体の作っている GRUB (グラブ)というソフトウェアです。
なのでブートローダをソースコードを探す場合も、Gru Grub のウェブサイトを探す必要があります。
書籍だとアスキー出版『Linuxのブートプロセスを見る』などの題名の書籍で Grub を紹介してるので、ついつい何となく、ソースコードを読むためにリーナスの管理している kernel.org を探しがちですが、しかし、よくよく考えたら、Grub は Linux ではありません(実際、Windows版Grubもある)。
もし『Grubのブートプロセスを見る』だと売れないので、出版社が「Linuxのブートプロセスを見る」という題名にしたのでしょう。
私たちOS開発をしたい読者は、けっして出版社にマインド・コントロールされたままでは、イケません。真実「Grub は Linux ではない」に気がつきましょう。
Grub のソースコードのダウンロードにgitコマンド(git clone などのコマンド)が必要なので、あらかじめインストールしておくか、Gitコマンドが最初から使える Ubuntu か Fedora などのLinuxをパソコンにインスト-ルしておきましょう。
Git コマンドのインストールと、Git Hub などのウェブサイトとは別物ですので、混同しないようにしましょう。
外部リンク: Gitの公式サイト
なお Windows版のGitはVimエディターにしか対応してないとの情報がインストール時に出ますが、しかしわざわざvimを別途インストールしなくても、Win版GitをインストールすればWindwowsコマンドプロンプトでGitコマンドを使えるようになります。