2006年4月アーカイブ

6 章の続き。

割り込みの仕組みはざっとこんな感じ。外部割込みは PIC という装置が CPU とつながっていて、割り込みの種類 (IRQ) は全部で 16 個あると。PIC と CPU はどういう風につながっているかというと、割込み信号ピンが 1 つと、何番が割り込んだを伝える 2 バイトのデータを伝える信号線の 2 種類の信号線。CPU は割込み信号で割り込みの有無を検知し、同時に PIC からの 2 バイトデータで何番の IRQ が割り込んだかがわかるようになっている、という具合みたいです。で、その番号を伝える信号線から送られる 2 バイトのデータは実は CPU の INT 命令そのものになっていて、PIC から送られるデータをそのまま実行してハンドラに飛ぶとのこと。なんかすごい離れ業ですね、コレ。

また、割込みハンドラでは

  1. 全レジスタをスタックに退避
  2. 割込みで行いたい処理を実行
  3. スタックからレジスタの値を復帰
  4. IRETD 命令で終了

の処理を行います。そのうち 1, 3, 4 はアセンブラで実装。2 は普通の C の関数。レジスタの退避 & 復帰は割込み処理が終了した時点で割込み前の状態に戻すためのもの。そして最後の仕上げとしてハンドラを IDT に登録して、マウスとキーボードの割込みマスクを解除し、割込み禁止フラグをクリアすることで、実際に割り込みが起こったときにハンドラが初めて実行されるという具合です。じゃあ、マウスとキーボード以外の割込みはどうするかというと、マスクして CPU が割込みを受け付けないようにしておきます。そうしないと、静電気などのノイズで割り込みが入ったときにハンドラが登録されてないとかで変なことになりますので注意が必要とのこと。

これで、やっとキーボード割り込みを受け付けることができました。長かった…。でも、まだマウスの割り込みは受け付けられず。IRQ12 が上がってきてない模様。

6 章終了。ハードウェアの話も出てきましたし、この章から急に難易度が上がりました。

6 章: 分割コンパイルと割込み処理

分割コンパイルはこれまでに幾度となくやってきて、そのメリットなんかも充分に理解できているのでさっと読み飛ばしました。

6 章のメインは、機能の GDT, IDT の説明と、割込みハンドラの作成。

まずは GDT から。これは

  • セグメントのサイズ
  • セグメントの開始番地
  • セグメントの管理用属性

を 8 バイトに設定します。ただし、CPU の後方互換のためにサイズや開始番地は連続する領域ではなく、細切れに設定することになっています。今回のプログラムではこの設定を構造体を介して行っているのですが、特定のアドレス領域に C の構造体で値を設定する場合はメンバ変数のアライメントが気になりますね。なりませんか?私は気になります。

GDT の先頭アドレスは開発者が自由に決めることができますが、GDT を設定するための構造体は short, short, char * 4 の構成になっています。ということは先頭を奇数番地にしたらまずいんじゃないかなと思ったので…

実際にやってみた (トリビアの泉風で)。

結果、GDT の先頭アドレスを奇数番地にしても、とくにエラーもなく実行できてしまった…。

コンパイルして作られたアセンブラを見てみると、

    MOV    ESI,2555905  <-- GDT の先頭アドレス(奇数番地)
    MOV    EBX,8191
L6:
    PUSH   0
    PUSH   0
    PUSH   0
    PUSH   ESI   <-- 先頭アドレスをスタックに積む
    ADD    ESI,8 
    CALL   _set_segmdesc

とあって、_set_segmdesc では

_set_segmdesc:
    PUSH   EBP
    MOV    EBP,ESP
    PUSH   EBX
    MOV    EDX,DWORD [12+EBP]
    MOV    ECX,DWORD [16+EBP]
    MOV    EBX,DWORD [8+EBP]    <-- 2555905  
    MOV    EAX,DWORD [20+EBP]
    CMP    EDX,1048575
    JBE    L17
    SHR    EDX,12
    OR     EAX,32768
L17:
    MOV    WORD [EBX],DX <-- 奇数番地にワード書き込み

となっています。確かに L17 の時点で EBX には 2555905 が入っているから奇数アドレスへのワード書き込みで吹っ飛びそうな気がするんだけど…。x86 はアライメントルールなしですか?

ということで、はるばる Intel のサイトまで出かけて Pentium4 のドキュメント(pdf)を調べてみると 4.1.1 Alignment of Words, Doublewords, Quadwords, and Double Quadwords にこんな記述がありました。

Words, doublewords, and quadwords do not need to be aligned in memory on natural boundaries. The natural boundaries for words, double words, and quadwords are even-numbered addresses, addresses evenly divisible by four, and addresses evenly divisible by eight, respectively.

ということで、アライメントルールはないことが分かりました。

( ・∀・)つ〃∩ ヘェーヘェー

もちろん補足トリビアも用意してございます。上で引用した部分の続きには、

However, to improve the performance of programs, data structures (especially stacks) should be aligned on natural boundaries whenever possible. The reason for this is that the processor requires two memory accesses to make an unaligned memory access; aligned accesses require only one memory access.

とありました。アライメントルールはないものの境界を跨いだメモリアクセスなど出来るわけなく、実際には CPU が頑張ってと 2回メモリアクセスするので、(特にスタックへのアクセスは) 効率悪くなりまっせということらしいです。

( ・∀・)つ〃∩ ヘェーヘェー

私はアライメントエラーで吹っ飛ぶ CPU しか知らなかったので、ちょっと Intel 系 CPU の真面目さにちょっと感動しました。

以上、6-5 まで終了。割込みハンドラはこの後すぐ!

5 章: 構造体と文字表示と GDT/IDT 初期化

VRAM に表示する方法は出来たので次は文字表示、I/O に依存する printf は使えないけど sprintf は I/O に関係ないので、コンパイラ付属のライブラリからリンク。フォントデータは '*' と '-' からなるアスキーアートで「A」などを書いたテキストファイルから生成。マウスカーソルも同じように生成し、文字とマウスカーソルの表示に成功。

次に「セグメント」のお話。メモリアドレスは「0x00000000 から 0xffffffff」まであるけど、このままでは、将来複数のプログラムを実行するときに、いちいち開いているアドレスを探してそこにロードするという今年なくてはならない。それを回避するひとつの技がセグメントということらしい。具体的にはメモリ空間をセグメント単位で分けることで、セグメントの先頭を 0 番地として扱うことが出来るというもの。セグメントごとに実行プログラムを配置して実行すればアドレスが重なることなく実行できるということらしい。仮想アドレスを実現するページングはこの本では取り扱わないとのこと。

セグメントについてかいつまんで説明すると

  • 開始アドレス、サイズなどの各セグメントの情報のサイズは 8 バイト
  • セグメントの個数は 8191 個。どのセグメントを利用するかは セグメントレジスタ(16bit) の上位 13 ビットに 0 ~ 8191 の値を設定し、指定する。
  • すべてのセグメント情報、つまり 8 バイト× 8191 = 65536 バイトはメモリ上に確保する。
  • 65536 バイトのテーブルを GDT(= global (segment) descriptor table) と呼ぶ。
  • そして、GDT の先頭アドレスは GDTR に設定しておく。

とのこと。肝心の segment が括弧という GDT の名称が少し気になりますが…。まあそれは置いといて、これで

GDTR + セグメントレジスタに入っている値 * 8

で各セグメントの情報を取得できるという算段のようだ。

次 IDT。interrupt description table の略で、これは全部で 0 ~ 255 のエントリからなる外部割込み用のベクタテーブルのようだ。

5章は GDT、IDT について概要の説明とそれぞれを設定するプログラムを作成して終わり。6章以降で実際に割込み制御などを行っていくようだ。が、まだ 8 バイトの中身の説明などは詳細の説明がないので、個の後も延々説明が続くような気がする。

5日目にして急に内容が難しくなってきた。でも、ポインタ変数が明後日なアドレスを指していたときに Segmentation fault でプログラムが止まる理由が少し分かったような気がします。セグメントのサイズを越えてアクセスしようとしたときに出るエラーって事だな。

5章終わり。

でも、トータルリコールでもいいのですが(どっちみち作者同じですし)、それらの作中に出てくるような
テクノロジのひとつである、顔面認証の実験が霞ヶ関の駅で始まるという記事を engadget で見つけました。Read 先の 記事を読んでないので詳細は分かりませんが、テロ対策とかが主たる目的のようですが(大義名分ともいうが)、ついにというか、とうとう来ましたかという感じがします。

写真が霞ヶ関ではなさそうだわ、記事のまとめも微妙にオチていないわで、はじめはネタかとも思いましたが、そうではないみたいです。

テロ対策とか施設への入退出管理ぐらいならいいけど、顔面認証で改札はちょっと気持ち悪いかなぁ。僕らの受け入れ準備はまだ出来てません。

今日は 4-6 から。アセンブラといえば、割禁。割禁といえばアセンブラというほど、重要な割禁(割り込み禁止)が、ようやく出てきました。割り込みフラグは EFLAGS レジスタの ビット 9。EFLAGS には他にもキャリーとかゼロフラグなどがあります。ということで、フラグレジスタも登場しましたね。割禁の許可、禁止 は 1 命令で出来るらしい。とか説明しておきながら、CPU の割込み動作事態の説明は後でするとのこと。今回は VGA のパレット設定のときには割禁にしなくてはいけませんというお話だけ。おそらくキーボード、マウスの制御まで割り込みの詳細な話はお預けの模様です。あくまでも流れ重視の OS 作りです。30 日で終わらせるというのですから流れ最優先、それは仕方ないことです。この流れ重視の体裁は、伏線が多いミステリーなもので、OS 作りとは別の意味でドキドキしています。もちろん撒いた伏線は全部回収してくれると信じてます(そうでないと困ります)。

パレットの読み書きは特定のメモリにパレット番号を設定して、特定のアドレスにデータを R,G,B の順に 3 回書けばオッケイ。このとき命令は MOV じゃなくて、OUT を使うとのこと。つまり、PC の CPU はメモリアドレスとと I/O アドレスが別々に I/O マップド I/O 方式なわけです。メモリアドレスも I/O アドレスも同じ空間を使うメモリマップド I/O 方式ならなら入出力全てを C 言語で書けるわけですが、今回は入出力用の関数をすべてアセンブラで用意することとなりました。

以上、4-6 終わり。

4 章: C 言語と画面表示の練習

今日は C からアセンブラの関数を呼び出しの第 2 段。引数のある関数の呼び出し方。x86 がということなのか、今回が特別なのかは分からないけど、引数はすべてスタックに入るご様子。これは、C言語の「関数呼び出し時、引数はスタックに積まれます」と習ったまんまやね。ビルド過程で作られるリストファイル(アセンブラと機械語が並べて書かれたファイル)を見ながら、どういう風にアセンブラに変換されているのかを理解。

あとはポインタ変数を使って VRAM 領域にアクセスして直接描画する仕組みと、C 言語におけるポインタそのものの説明が続く。このあたりは超楽勝。僕もポインタを勉強するのにはアセンブラはいい方法のひとつだと思います。以上で、縞模様の画面を書くことが出来ました。だから、縞模様は OS には関係ないんだけど、VRAM にポインタでアドレス指定してアクセスするってのが OS 作りとしてはミソになるのかなぁ、と考えつつ 4-5 まで終了。 ほとんどポインタの説明だったからこれといって書くことはありませんでした。

3 章: 32ビットモード突入と C 言語導入

まずは、フロッピーの作りについて。同心円状のシリンダが 80 個あって、1 つのシリンダに 18 個のセクタがあって1 つのセクタが 512バイト。それが両面あるから

80 * 2 * 18 * 512 = 1474560 byte

これがフロッピーの容量ということになる。フロッピーのロードもシリンダとかセクタをレジスタで指定してINT 命令で行うとのこと。

2 章途中から。

文字を表示させるには

MOV AH 0x0e
MOV AL キャラクターコード
MOV BX 0x000f
INT 0x10

とするらしい。INT 命令はソフトウェア割り込みの命令で BIOS の機能を呼び出すらしい。 で、0x10 はビデオカード制御に関する割込みということのようだ。BIOS って、IPL とかブートデバイスの設定とかするだけだと思っていたら、実はこの辺も全部やってくれているらしい。でも、よく考えてみたら BIOS = Basic I/O System なんだから、そうだよな。

次にメモリマップの話。ORG 0x7c00 ってブートセクタは 0x7c00 - 0x7dff の 512 バイトの領域だそうだ。なんで、このアドレスかというのはこれは Intel のおっちゃんがそう決めたからということ。勝手に決めたというても、ハードのアーキテクチャの話も絡んでくる話だろうから、しょうがないわな。

あと、hello world を表示するだけのプログラムの最後は

fin:
   HLT
   JMP fin

となっていて、HLT を入れることで CPU が止まって(割込みは受け付ける)、HLT のない無限ループと違って電気代の節約になるということが書かれていた。この考えには私も大賛成。ただ単純に無限ループが気持ち悪いというのも歩けど、ドライバとか OS とか下っ端のプログラムはこういうところも気をつけないとね。裏方がしゃしゃり出てはいけません。必要がないデバイスの電源は OFF にする。CPU ももちろん例外じゃありませんよと。携帯電話で閉じた状態のときにバックライトが OFF になるだけで液晶自体は表示されたままのがあるんですよね、そういうのはちょっとなんで…と思いますよね。特許回避のためかもしれないんだけど。

2 章の残りは Makefile の説明があって終わり。

Engadget Japanese の記事からロックマンエフェクトをご紹介。タイトルのランチャーというのは実は嘘で、実際にはアプリ起動時のエフェクトのみ。もちろんあの BGM も流れます。インストールして試してみましたが、バカすぎて面白いです。でも、当たり前ですが普段は使いません。その昔、ルパン3世の アニメのタイトルを任意のメッセージで表示できるアプリがありましたが、そんな感じのお遊びソフトです。

いや、だからなんだって言われても困るのですけど。

2 章:アセンブラ学習と Makefile 入門

昨日書いたブートセクタのアセンブラは 1 バイトのデータを書く DB 命令、ワードのデータを書く DW 命令、領域を確保する RESB 命令のみで書かれていたので、実質バイナリとそれほど変わらないのだけど、今日はそれと等価できちんとした?アセンブラの紹介。でも昨日書いた DB だらけのプログラムを MOV 命令などで書き換えたものが突然出てきたので、「え?これなんのプログラム?」と一瞬戸惑った。

<< 1 2 3

最近の画像

Zero History - ギブスンの新作が 9 月に発売 -
虐殺器官
ドミニオンのランダマイザアプリ、dominion minion が日本語対応!
ハイペリオンの没落 ~ 完結...でもまだ続く~
ハイペリオンの没落 ~ 完結...でもまだ続く~
ハイペリオン - 長大な叙事詩 SF のスタート -
ハイペリオン - 長大な叙事詩 SF のスタート -
「都市と星」 - 10億年と言われてもよく分からない -
「星を継ぐもの」 - 良い Sci-Fi -
トールサイズのニューロマンサーを買った
虎よ、虎よ! - 主人公フォイルの執念の物語 -

私の本棚

 

自転車走行距離

自転車走行距離

Flickr Photos

FlickrPhotos

なかのひと

Y!ログール

このサイトについて

2011年10月

            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          

アーカイブ

My Update

  • Loading...

その他

あわせて読みたいブログパーツ