はりぼて日記
2007 | 01 | 02 | 03 | 04 | 05 | 07 | 08 | 10
2006 | 11 | 12
10月 10 (水曜日) 2007
22:57
22日目3
 
さてそんなわけで、だいたいなにをやってるかがやっと理解できました。

P467
なるなる〜。
内容はちがえど、これまでと同じ手法ですねぇ〜。

そんなわけで、22日目終了〜
 
30日を過ぎたBlogにはコメントできません。
20:50
22日目2
 
------------約1時間後-----------------

・・・あーー! なるほどなるほど!
そういうことかぁ!

P439の_asm_hrb_api。ここを見てみてやっとわかった!

例えばだけど、現在のスタックの番地(ESPの値)が仮に0x1234だと過程する。この状態でPUSHADを二回行うと・・・


0x11F8 ediの値     ┬   ←現在のESPの値
0x11FB esiの値     │ (つまりスタックの番地)
0x1200 ebpの値     │
0x1204 espの値   2回目のPUSHAD
0x1208 ebxの値     │    
0x120B edxの値     │
0x1210 ecxの値     │
0x1214 eaxの値     ┴
0x1218 ediの値     ┬
0x121B esiの値     │
0x1220 ebpの値     │
0x1224 espの値   1回目のPUSHAD
0x1228 ebxの値     │    
0x122B edxの値     │
0x1230 ecxの値     │
0x1234 eaxの値     ┴


  • スタックはある番地を開始点として、PUSHされるごとに上の番地に進んでゆく(小さい番地へ向かう)
  • PUSHされるごとに番地は4づつ減ってゆく(4すなわち4バイト。4バイトとは32bit。32bitレジスタであるeaxなどの値がぴったり収まるようになっている)
  • P133、P134を参照のこと


こういう状態の後、C言語であるhrb_apiに渡される。
関数hrb_apiは各引数を、順にESP+4の倍数としてスタックから受け取る。つまり、0x11F8から int ediを、0x11FBから int esiを・・・・0x1214から int eaxを。こうして受け取っている。

・・・ここで、

int *reg = &eax+1;

という命令を行うと、ポインタである*regには引数である int eaxのお隣、すなわち0x1218が設定される。
(*regをint型で宣言しているため、4バイト刻みになる。なので、+1するということは4バイト先ということになる)

hrb_apiの後ろのほうで、

reg[7] = (int) sht;

という処理をしている。これは配列という意味ではなく、*regから7個お隣と言う意味として使われている。

すなわち、0x1234番地にshtの値が書き込まれたわけである。

この処理の後、hrb_apiはreturn命令で戻る。どこへ戻るのかといえば、自分を呼び出したasm_hrb_api(アセンブラプログラム)に戻ってゆく。

処理が戻ってきたasm_hrb_apiはアプリ終了かそうでないかを判断した後、

  ADD    ESP,32
POPAD

という処理をする。これはどういうことかというと、
本来ならPOPADを二回行うはずである。(だってPUSHADを二回やってるから。そうしないとつじつまが合わない)

しかし、当たり前だがPOPADはレジスタの値を書き換える。二回目にPUSHADした値はC言語実行の後だし意味がない。まるごと戻す必要がないのだ。

そこで、POPADはするがレジスタは書き換えない。そのため、ADD ESP,32を行う。これで、ESPの値すなわちスタックの番地が0x11F8から0x1228になる。

この状態でPOPAD!

レジスタには、0x1228〜0x1234の値が書き込まれ、元の状態に戻る。
(ただし、本当に元の状態ではない。0x1234の値は書き換えられている。)

これでIRETDを行い、処理を割り込み前のapi_openwinに戻す!

アセンブラプログラムであるapi_openwinは後処理をし、呼び出し元に戻る。

api_openwinを呼び出したのはC言語であるwinhelo.cである。
処理が終わると変数winに現在のEAXの値を代入して処理を終える。


・・・・ぷっはあぁぁぁぁ! やっとこさ整理できたーー!


・・・うーむ。ここいらへんはアセンブラやCを行ったり来たりなのでややこしいなぁ〜・・・
 
30日を過ぎたBlogにはコメントできません。

Referer  (11)
13:26
22日目
 
22日目〜。

さらにOSを守る。

P446
ふーむ・・・
IN・OUT命令はアプリからは使えないと。(使えるようにすることもできなくはないよう・・・)

そう考えると、ドライバなどはどう考えたらいいのかな?
ドライバはアプリとすることは出来ない。(上記の理由で)
ということは、ドライバはOS側ということになるのかな。
Win等の市販のOS(サードパーティーがドライバを提供するのが
当たり前になっているOS)ではどう考えているんだろう???

P449
黒川合退散〜 (笑
アプリ作成再開!

P451
・・・むむむ!??
あれれ? これけっこう重要and知らなかったかも?
C言語で配列を宣言した場合、それはメモリではなくスタック内に設定される(まあスタックも、メモリっちゃあメモリだけどそこは分けて考える)
っていう理解でいいのかな???
普通の int a; とかはどうなんだろう?
こっちはメモリなのかな???


P453
うーむ。おもしろいなぁ。
割り込み発生時に各レジスタの値も表示すると・・・
(今思うと、OSASKのバグ表示もこれだったのかな)

P458
・・・あれれれ?
例の6バイトのことだ。あっれーー? なんか見落としてる?

ちょっと戻って整理してみよう。

  • P424。アプリの頭6バイトを書き換えないといけない。なぜ?
  • アプリの本体(実行コード)の前で0x1bをコールしなくてはいけないから。なぜ?
  • .hrbという実行ファイル。プログラムそのものが先頭から始まっているわけではない。先頭0x00〜0x1aまではプログラムではないのだ。なぜ?
  • ・・・コンパイラの設定or仕様なのか???・・・
  • とにかくメモリにロードされたアプリは0x1bからコードがスタートする。なので、そこをコールしなくてはいけない。なぜコール?
  • RETFしないとこのアプリを呼び出したコンソールに戻れないから。なのでgotoではなくcallして、戻ってきた次の場所でRETFしている

・・・ふむ!! とりあえずこんな理解でいいのかな。
で!!!


  • P441。いままではRETFをかけることで、アプリにジャンプ
    してきたコンソール(OS側)に戻ることができた。
  • いままではアプリもOSのセグメント内(OSと同じ権限を持つ別のセグメント)で動いていたのでRETFが使えた。なので使い、ちゃんと戻ってきた。
  • セグメントをアプリ用に設定してしまったのでこれでは戻れない。終了してコンソールに戻るにはそれ用のAPIを呼び出す形を取る。



  • P458。もうRETFでは戻れないOSとなった。不要。
  • なのでstart_appで書き換える必要はない。直接そこへジャンプすればアプリはスタートする。
  • 逆に言えば、以前はRETFをする必要があったのでstart_appで直接コールすることが出来ないorそれじゃ足りないと。


・・・こんなところかな???・・・・・・



P459
ふむ・・・・このあたりから、.hrb(実行ファイル)の内部構造が
解説されてるね。
なるほどなぁ。実行ファイルって、ただ最初からベターっと
プログラムが書かれているわけじゃないんだね。
また、なんのためにそういう仕組みになっているのかもこれを
読むと良くわかる。
ついでに、なぜ、先の6バイトで0x1bをコールしているのかも
ここで解説されとりますね。


P463
へえぇぇ。
.textと.dataかぁ。なんかややこしいですねぇ。
(まあ紛らわしいってのはもう、しょうがないんでしょうね。)

P465
へえぇぇぇ! こりゃおもしろい!
PUSHとPOPって別に絶対的なことじゃないんですねぇ。

ん??? あれぇ?
P466のwinhelo.cのソースでapi_openwinからの戻り値を取ってる。(int win)
ここには何の数値が入る?。どこでその処理をしてる???
もしかしてCとアセンブラの連携だったっけ?。ちょっとメモを見よう・・・

えーーーっと!
Cからアセンブラの関数を呼んだ場合、パラメータが左から順に、
[ESP+4]、[ESP+8]、[ESP+12]・・・と格納されると。
さらに、アセンブラ内ではEAX・ECX・EDXしか使えないと。
(P466のa_nask.nas内では使っているが、やっぱり本来は使っちゃいけないレジスタなので、EDI・ESI・EBXをスタックに退避させ、API割り込み呼び出し後にPOPして復元してから終了してるわけ)
・・・あ”こっちかぁ。
P465のconsole.cのほうかぁ。

あーーー! なんかちょっと読み飛ばしてたみたいだ!

P464でちゃんと、EAXに戻り値が設定されて帰ってくるってことにしてるんじゃん!


  1. Cのアプリであるwinhelo.cから api_openwin がコールされる。
  2. api_openwinの中身はバッファの整理。パラメータをレジスタに設置してINT 0x40し、割り込みを発生させる。
  3. 発生したのは割り込みであるので、OSがそれを検知して割り込み処理を行う。
  4. 0x40の割り込みが発生した場合の動作はIDTに設定されている。asm_hrb_apiが設定されているのでここに制御が移る
  5. asm_hrb_apiはレジスタを保存。さらにDSやESをOS用に切り替えてhrb_apiをコールする
  6. hrb_apiは動作を行う。

  7. 動作を終えると、逆順で戻っていく



えっと。これを踏まえて・・・
 
30日を過ぎたBlogにはコメントできません。

Referer  (1)
11:58
21日目
 
21日目〜。

P422
あったり〜。 (^^

P423
さーていよいよC言語に突入だぁ〜。

ふむふむ。これも重要だよね。アセンブラとのリンクのやり方。

・・・あれ? んーーと?  先頭の6バイトのことはもう少しあとだっけ???

P427
OSの保護とな? うーむ。理屈はわかるぞ!

P432
了解〜。ご指南どおり、とりあえず軽〜く流します〜(汗
(とはいえ、なんとなく、なにをしているのかはわかるぞ。とにかく、各コードが動く時にレジスタを調整し、アプリ実行時とOS側の動作時にレジスタやセグメントが混ざらないように徹底的に分離しているわけか。)

P440
たははは・・・。軽く流しておいてよかった。全部自動でできるじゃん!!(^^


P441
0x40を、アプリ側から呼んでも構わないという設定にするのね。

(ふと思ったが、他の空いている割り込みはどうなんだろ?。この時点での
ことはともかく、最終的には全部それなりに設定しておくべき
なのかな? それとも??)

他のソースはわかるよね。
ようするに、各コードのお尻に終了用のAPIをコールさせると・・・。


うーむ。コードはけっこう難しかったけど、わりとよくわかったなぁ。

そんなわけで、21日目終了〜
 
30日を過ぎたBlogにはコメントできません。
11:16
20日目2
 
20日目2〜。

さて例の呼び出し先の番地の問題。

・・・おぉぉ! なるほど。割り込みを使うのか!

P410
お? ここ重要かも。(後で忘れてハマりそうな悪寒・・・)


CPUは割り込みが発生すると自動でCLI命令を実行し、以降割り込みが禁止される。なのでこれが必要ない場合は呼び出された割り込み命令
の先頭でSTI命令を発効しておく

なお、この処理は本来はIDT登録の折、パラメータかなにかで
設定が可能&本来はそうするべきらしい。



P415
・・・むむむ?
わかるけど素朴な疑問。
なんでこの処理でECXレジスタの値が壊れちゃうんだろ??
今はとりあえずそういうものだということにして後でよく考えて
みよう。

ここは棚上げ〜

P418
いえいえ〜。Kタンだけじゃなくてオイラも読みやすいっす。(^^

このあたり。つまり機能番号で分岐するように多段化したわけですね。しごくごもっとも!


・・・でも失敗・・・

んんんん??  なんでだろう?

次に進む前にすこし自分で考えand思い出してみる。

・・・セグメントじゃないかな・・・

だって、一文字表示は文字コードをレジスタで渡しているけど、
文字列表示の場合、渡されるのは文字列が格納されている番地
(ポインタ)でしょ?
呼んだ者(アプリ)と呼ばれた者(OS内のAPI)ではセグメントが
違うから、うまくいかないんじゃないかな・・・

さてさて???

そんなわけで20日目終了〜
 
30日を過ぎたBlogにはコメントできません。
(1) 

PopnupBlog V3 Denali created by Bluemoon inc.