はりぼて日記
12月 26 (火曜日) 2006 | ||
10:03
|
六日目3
|
|
六日目 [5] 終了〜。
・・・うむむ。たしかにここいらへんはむずかしいなぁ。 でも、この本はできるだけ説明をしようとしているのでそう見えるだけかも。少なくともこのセクションは、 ・IDTを設置して、割り込みを受け付ける準備をしたぞ〜。 ・CPUの割り込み受付ピンは一本しかないので補助の回路が付いてる。 ・補助回路を初期設定しないといけないんだぞ〜。 ・補助回路はPIC0、PIC1の二つあるぞ〜。 ・まずはPIC0、PIC1に、「初期化中だから一旦全ての割り込みを禁止」にさせろ。(IMRの設定) ・PICの設定開始! ・IPC0、PIC1には四つの設定項目がある。(ICW1〜ICW4) ・ICW1、ICW3、ICW4は、まあ特殊なことしないなら普通は決まった値を設置する。 ・で、肝心なのがICW2。つまり、これで「割り込みが来たら、どこを見ればいいか?」と設定する。 ・IPC0(割り込み0〜割り込み7を担当)には、「割り込みがきたら0x20〜0x27を見ろ!」と設定。 ・IPC1(割り込み8〜割り込み15を担当)には、「割り込みがきたら0x28〜0x2fを見ろ!」と設定。 ・割り込みの設定ができた〜! 割り込みの受付を再開していいぞ〜(IMRを再設定) ・・・とまあ、こういうプログラムなわけね。なるほど〜。 |
||
09:32
|
六日目2
|
|
六日目、[4]。
なんと中ボスらしい! (ところでアナタにとって中ボスといえばなに!? 私の場合はやっぱりフドウミョウオウだったりします。←メガテンマンセー) うーむ。これはようするに、前回出てきたGDT表。この表の各項目の設定方法の説明だな。 ・セグメントの大きさ ・セグメントの開始番地 ・セグメントの属性 この三つで一つの行になっているわけか。その理屈自体はまあ、よくわかるな。 ・・・で、問題点。なるほど。過去の資産(80286)との互換性を維持するために、えらくややこしい(むずかしいというよりは)方法でないといけないんだな。(インテルのCUPが嫌いだっていう人は、こういう部分に泣かされた人達かな???) たとえばだけど、そういうちょっとヘンテコな仕様じゃなかったら、きっとこんなんでいいはず。
base、limit、access。おのおの設定に必要な数値から考えるとこれで十分なはずだけど、実際にはCPUがややこしい仕様になっていて、数値を別々に割って設定しなくてはいけないって感じかな?? |
||
08:32
|
六日目
|
|
六日目 〜3。 終了〜。
C言語で書かれたプログラムのソースは大抵いくつものファイルに分かれていますね。それはどういう理屈なのかってこと。 ここいらへんはだいたいOKかな? ツルっと読み終わりました。 |
||
12月 23 (土曜日) 2006 | ||
17:18
|
五日目4
|
|
五日目 9〜。
セグメンテーションか。えとえと??? これがややこしいんだよなぁ。元来は便利な機能なんだけど。P112の上のほう。 MOV AL,[DS:EBX] えーっと。この場合、[]内が示す番地のデータをALにコピーしろと。 で、「DS:EBX」と書かれている場合。32bitになっている現在は、DSレジスタがあらわすアドレス+EBXの値ってことでいいのかな? たとえばDSが 0x1000。EBXが0x0200だとすると、ALレジスタには0x1200番地の内容がコピーされるって理解でいいのかな。 えーっと。P112の真ん中らへん。わかんね〜なぁ・・・ まてよ? パレットとそっくりだと・・・ こういう意味なのかな?。「セグメントレジスタ」に設定するのは、セグメント番号。このセグメント番号っていうのは単なる番号。つまり、1番・2番・・・・8000番までありと。で、さらに、この1番や2番という番号(数値)はなんにもアドレスとかとは関係ない。単に「何番目か?」っていうだけ。じゃ、セグメントレジスタに、3を設定したとする。その場合、実際には何番地を読み込むのか?。 「いや〜。3番は0x0100からだよ。あと、4番は0x8000だよ」 って感じで、勝手に好きに設定できるっていう意味なのかな?? あ〜! わかった。そういうことか!!!。つまり、これは単なる表なんだ!。メモリのどこかに、ズラーっと8191個分の表を確保し、そこに実際の開始番地や大きさ等の情報を設定しておくと。なるほどねぇ。 そうなると、たとえばセグメントレジスタに4を設定する。そして0x0100番地を読み出そうとしたとする。その場合、CPUは本当のホントの0x0100番地を読むのではなく、GDT表の4番目を調べる。たとえばここに0x2000が設定されていれば、実際には0x2100番地が読み出されると。そんな感じかな?? たとえばだけど、メモリが1GBあるパソコンだとしよう。GDT表の一番目に、開始番地0x0000・大きさ512MBを設定し、二番目に開始番地0x8000・大きさ512MBを設定すると、このOSではセグメントが2個しか使えない!なんて理屈になると。(実際はシステムで使っている部分とかがあるからそんな単純じゃないんだろうけどね。理屈としてはこんな感じでそ?) なるほど。この感覚とほぼ同じ感じでIDTがあると。IDTも表って考えていいかな。こっちは「割り込み番号と実際に実行されるプログラムが書いてある番地」ってことね。 ここも、たとえばだけど、全てのIDTに0x5000って書いたとすると、キーボードだろうがマウスだろうがどんな割り込みが起こっても全て同じ動作をするなんていうおかしな状態にすることもできると。 なーるほどねぇ。つまり、GDT(セグメント設定用の表)と、IDT(割り込み時の実行プログラム位置の表)を設置しなくてはならない。で、この二つの表は、空いていれば好きな場所に設定できるわけですな?。 ・・・あれれ??? ここでとりあえず終了?? ほ・・・よかった〜。つーわけで、とりあえず五日目終了〜。 |
||
15:03
|
五日目3
|
|
五日目 〜8 まで終了〜。
ふむ! ここは幸いあまり難しくなかったなぁ。 (とはいっても例によってすこしあやしいんですけどね。(^^; ) ・・・嗚呼!それにしても、グラフィックがだんだん出来上がっていくのを見るとつらいなぁ・・・ フォントやマウスや画面デザインをいじりて〜! |
||
12月 22 (金曜日) 2006 | ||
17:24
|
五日目2
|
|
五日目 〜2。
うーむ。構造体そのものはそんなわけで解っている。structを使って定義し、HariMain内で宣言して使えるようにすると・・・ struct BOOTINFO *binfo; ・・・え”? *binfo? binfoじゃなくて? *? え〜???? ひええぇぇぇ!!! わっかんね〜!!(泣 ・ ・ ・ ・・・ぁ〜 あ〜 あぁ〜!なるほどそういうことか! 前後の読み込みが足りなかった。なるほど。これは、「俗にいう変数宣言」じゃないんだ。極論すれば、「メモリのアドレスに名前をつけて扱いやすくしたい」というだけのことなのか!。勘違いしてた。 ええと、まず、一個前のソースでやってるよね。つまり、 binfo_scrnx = (short *) 0x0ff4; とか。これはどういう意味かって言うと、書き方が特殊なので「あれれ?」と思ったけどなんのことはない。 「メモリ番地 0x0ff4〜0x0ff5(2バイト分)を、binfo_scrnxっていう名前でアクセスできるようにしてくれ」 ってだけのことじゃマイカ。例の「型キャスト&省略」タイプの書き方なのでわからんかった。キャストや省略を無視して書けば、 short *binfo_scrnx; binfo_scrnx = 0x0ff4; と、こうしているだけなんですなぁ。なるほどなるほど。 で!!!! 同じようなことを構造体で一まとめにしてやっちゃおうってわけね。なるほど〜。 ちょっと変則的だけど、私はこんなふうに理解してみた。 たとえば、 struct BOOTINFO binfo; と普通に「binfo構造体」を定義する。その場合、定義どおりに、あるどこかのメモリ番地にbinfo用のエリアが予約される。(これはたぶん、毎回不定のはず。)じゃ、たとえば解りやすく、0x1000に割り当てられたとしよう。そうすると、 0x1000 char cyls 0x1001 char leds 0x1002 char vmode 0x1003 char reserve 0x1004 shor scrnx 0x1005 〃 0x1006 shor scrny 0x1007 〃 0x1008 char *vram (ポインタすなわちメモリアドレス用なので4バイト) 0x1009 〃 0x1010 〃 0x1011 〃 っとまあ、こういう感じになるわけだ。 ・・・・・で!!!!! この、毎回ちがうアドレスに設定される binfo構造体の開始アドレスを、0x0ff0 に強制的に設定したいわけ。(0x0ff0〜12バイトの部分には、asmhead.nasによってグラフィック関係の値がすでにセットされているので)そのための方法が、 struct BOOTINFO binfo; じゃなくて、 struct BOOTINFO *binfo; binfo = 0x0ff0; になるわけだ!(例によって型キャストとかはちょっと省略してある)なるほどなるほど〜!!! シンプルに分解したら、やっと解ったぞ! |
||
16:13
|
五日目
|
|
五日目〜。
うは! しょっぱなからむずかしいなぁ。構造体かぁ。 ええと、構造体そのものは幸い解る。ようするにこれは、配列だ。 たとえば、一年間の気温を記録したいとする。1〜12までと、数は決まっている。また、記録するデータも全部気温で同じようなものだから、12個の変数全ての型は同じでよい。そういう場合に、 int month[11]; とかやれば、12個のint型変数を用意してもらえる。 データを格納する時も取り出す時も、 a[8] みたいにすればいい。 しかし、もし、使いたいデータがみんな型がバラバラな場合どうしたらいいだろう? たとえば、「生徒の特徴」とか。 人の特徴だから、配列内に収めるデータは型がバラバラになる。 生徒「よしお君」 ┬─ 身長(int型) ├─ 体重(int型) ├─ 出身地(char型) └─ 年齢(Doble型) C言語では、変数は宣言時に型を決める。なので、上記のような場合、当然 int yoshio[3] ; とかやっちゃうと、出身地や年齢を格納できなくなってしまう。 int yoshio[3], ただし[2]はchar型,[3]だけSoble型; こんなことはできないだろうか? ま、それこそが構造体ということになるわけ。 構造体はさらに、こんな便利な要素もある。各変数を[2]とか[3]じゃなく、名前をつけて管理できる。これなら、「あれ? ええと、体重って[1]だっけ?いや、[2]だったっけ?」なんてことが起きないようになると。つまり、上記のような配列を作りたい場合、 struct 生徒データ { int 身長; int 体重; char 出身地; Double 年齢; } なんて宣言しておくと、「生徒データ」型の変数の配列を新しく定義できるわけ。「新しい変数型の定義」だから、以降は int a; int型の a という変数を宣言 char b; char型の b という変数の宣言 struct 生徒データ 義男; 生徒データ型の 義男 という変数を宣言 って感じで使えるようになると。こういうわけ。 配列内部へのアクセスも、普通の配列だと int a[11]; 配列の宣言 a[0] = 12; 一月の気温をセット a[1] = 14; 二月の気温をセット と、こういう感じになるけど、構造体の場合は struct 生徒データ 義男; 構造体「義男」を宣言 義男.身長 = 153; 義男の身長をセット 義男.体重 = 43; 義男の体重をセット と、こういう感じでできるようになるわけです。 (一応念のため書いとくけど、変数名等には実際は日本語は使えません) |
||
06:33
|
四日目3
|
|
四日目 終了〜。
やっべぇ!「OSっぽい」画面だ! う〜。いじりてー! まてまてまて。ここはグっと我慢。まだ先は長いんだから(汗 さて、この四日目で一番私が大事だと感じたのが、例の「アセンブラでCの関数を作っちゃう!」ってこと。 実は以前、私はOSの実験と学習のためにアセンブラで小さなブートプログラム(hideyOS 0.1)なんてのを作っていたことがあります。だんだん規模が大きくなってきて最後は収集がつかなくなって放置になってしまったのですが。 で、これに取り掛かる前、GCC(MinGW)でいろいろいじくっていた時のこと。どうしてもキーボードからの信号を受け取る方法がわからず、「こりゃ、アセンブラでやらないといけない部分だな」と判断して、GCC内でアセンブラを扱える「インライン アセンブラ」という機能を使おうとしたんです。 ・・・挫折・・・ いや、単に私の英語力や理解力が低いだけなんでしょうが、いくら調べても「チョコチョコ! パッ!」っとできる例や説明が見つからなかったんです。 当時の私は、「だめだ!インラインアセンブラは高機能なぶん、猛烈に複雑でかえってわからない!」と感じました。 その証拠に、その後、naskで書き始めたら数週間で目的を達成できたのです。(アセンブラそのものも勉強しながらプログラムしてた) しかし、やっぱりアセンブラ。プログラムしていくうち、漠然とではありますが、どんどん「・・・この調子じゃ、俺の頭が追いつかない・・・」になって来ました。 「naskでCの関数が作れたらなぁ・・・」 当時、そう思ったものです。 この四日目では、そのための方法と実例がばっちり書かれています。これを応用すればいろいろなことができるはず。 なんだか楽しみになってきました。 |
||
05:46
|
四日目2
|
|
四日目 〜5 まで終了〜。
うむむ・・・。ここがうわさのポインタ部分か。さらっと読むつもりだったけど、つい何回か読み直してしまった。 正直、ちょっと私の現在の理解はあやしいなぁ。でもとりあえず良しとしておくか。(進まないから) 現在の私の理解(まちがってたりたりなかったりの可能性大) ・メモリに直接値を書く必要がないなら、ポインタなんていらない! ・たぶん、ほとんどの普通のプログラムはポインタなんか使わなくても書ける。 ・OSなので、メモリに直接アクセスする必要がある。 ・Kタンがnaskfunkで作ったような関数があればポインタなんていらない ・しかしC言語ではそんな関数はない ・なので、ポインタというものを使う ・・・と、こんな感じ。で、さらに、 ●たとえばC言語に、<< アドレス番地 >> なんて命令があれば、*pなんてわけわからない書き方せんでもいい。 あるメモリに12を代入したいとする。(じゃ、たとえば0032番地とする。 アセンブラなら、 MOV BYTE[0x0032],12 私の「妄想C言語」なら、 <<0x0032>> = 12; で、実際のC言語の場合なら、 char *p; p = 0x0032; *p = 12; こう書かなくてはいけないと。そういうことみたいだな。キャストや演算( *(p+1)とかそういうの )は、便利に使うため。一旦忘れる!ってのが、理解を深めるのに役立ちそうかも?? ま、とりあえずこのくらいにしておこうっと。(こりゃ、早くも二順三順決定っぽいなぁ(^^; ) |
||
12月 21 (木曜日) 2006 | ||
00:21
|
四日目
|
|
四日目〜。
・・・その前に。先の三日目で疑問に思ったこと。(この本のことだからきっと後で出てくるのかもしれないので、この段階では無理に調べない。でも忘れちゃいそうなのでメモ。) ?? 16bitから32bitにスイッチされた場合、メモリの内容はそのままなのか ?? たぶんそうだろうとは思うけど、なにか変化や保障されない部分はないのか?? ・・・と、こんなこと。 さて、四日目だけど、一番最初ですこーし躓いた。69P〜70Pの、_write_mem8に関する部分。 一番目のもの: [ESP+4] 二番目のもの: [ESP+8] 三番目のもの: [ESP+12] 四番目のもの: [ESP+16] ??? あれ?これどういう意味かな??? あー。なるほど。そうかそうか! つまり、naskfuncみたいに「アセンブラでC言語から使える関数を作った場合」に、関数のパラメータの値が順番に格納されるわけね。パターンとして。 たとえば、私が hideyosi(int one,int two int three); こういう関数を考えてC言語側で定義したとする。パラメータは三つ。で、この関数を実際に使おうとして、Cのプログラムから sss = hideyosi( 1 , 2 , 3); と、こんなふうにコールしたとする。その場合に、 [ESP+4]、つまり(ESPの値+4)番地のメモリに、1が [ESP+8]、(ESPの値+8)番地のメモリに、2が [ESP+12]、(ESPの値+12)番地のメモリに、3が セットされると、そういう意味かぁ。なるほどなるほど。 |
||
1 (2) 3 »  |
PopnupBlog V3 Denali created by Bluemoon inc. |