はりぼて日記
2007 | 01 | 02 | 03 | 04 | 05 | 07 | 08 | 10
2006 | 11 | 12
10月 11 (木曜日) 2007
17:58
24日目
 
24日目〜



どどどど〜っと、ウィンドゥ関係の改造〜。

ちょっと読むのが大変だったけど、とりあえず引っかかるところは

なさそうだぞ。



P507

タイマー関係もOK。

進む〜♪



そんなわけで、24日目終了〜
 
name: @Guest  Comment:
10:38
23日目
 
23日目〜。



おは〜 (笑



メモリの確保かぁ・・・



P471



  • .hrbの先頭4バイトに大きさを書き込む

  • 0x0020にその開始番地を書き込む

  • あとはOSが自動的にセグメントを割り当てる



かぁ・・・。えっと。これはどこでやってるのかな??



あったあった。

P460の一覧表。そしてそれをP461のconsole.cで解析して、セグメント等の設定とメモリ確保をしているね。



他のコード。

大体意味はわかるな。



P474

点を書くAPI。

うむ!これは簡単だ!

これまでの応用or一部手直しでいけてるよね。



うーん。線を描く「隠し味」をもうすこし検証したいんだけど、

今回はこのまま。



あとは難しいところない〜♪



そんなわけで、23日目終了〜
 
name: @Guest  Comment:
10月 10 (水曜日) 2007
22:57
22日目3
 
さてそんなわけで、だいたいなにをやってるかがやっと理解できました。



P467

なるなる〜。

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



そんなわけで、22日目終了〜
 
name: @Guest  Comment:
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を行ったり来たりなのでややこしいなぁ〜・・・
 
name: @Guest  Comment:
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. 動作を終えると、逆順で戻っていく







えっと。これを踏まえて・・・
 
name: @Guest  Comment:
Referer  (1)
11:58
21日目
 
21日目〜。



P422

あったり〜。 (^^



P423

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



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



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



P427

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



P432

了解〜。ご指南どおり、とりあえず軽〜く流します〜(汗

(とはいえ、なんとなく、なにをしているのかはわかるぞ。とにかく、各コードが動く時にレジスタを調整し、アプリ実行時とOS側の動作時にレジスタやセグメントが混ざらないように徹底的に分離しているわけか。)



P440

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





P441

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



(ふと思ったが、他の空いている割り込みはどうなんだろ?。この時点での

ことはともかく、最終的には全部それなりに設定しておくべき

なのかな? それとも??)



他のソースはわかるよね。

ようするに、各コードのお尻に終了用のAPIをコールさせると・・・。





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



そんなわけで、21日目終了〜
 
name: @Guest  Comment:
11:16
20日目2
 
20日目2〜。



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



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



P410

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





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

の先頭でSTI命令を発効しておく



なお、この処理は本来はIDT登録の折、パラメータかなにかで

設定が可能&本来はそうするべきらしい。







P415

・・・むむむ?

わかるけど素朴な疑問。

なんでこの処理でECXレジスタの値が壊れちゃうんだろ??

今はとりあえずそういうものだということにして後でよく考えて

みよう。



ここは棚上げ〜



P418

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



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





・・・でも失敗・・・



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



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



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



だって、一文字表示は文字コードをレジスタで渡しているけど、

文字列表示の場合、渡されるのは文字列が格納されている番地

(ポインタ)でしょ?

呼んだ者(アプリ)と呼ばれた者(OS内のAPI)ではセグメントが

違うから、うまくいかないんじゃないかな・・・



さてさて???



そんなわけで20日目終了〜
 
name: @Guest  Comment:
10月 09 (火曜日) 2007
18:19
20日目
 
20日目〜。



ひさしぶりのソース整理の後は、いよいよAPIだ!



・・・ふむ・・・

APIはいわば関数の塊だよね。これをアプリからコールする。

そのためにはコールするための場所がわからないといけない。

また、レジスタ内に保持したパラメータ(この場合は文字コード)

はCとの関数のやりとりで壊れてしまうので、スタックに積むと。



コールする場所だけどその場所を記憶しておくところを作る。

(0x0fec)。なぜこんなことをしないといけないのか。

関数consの場所はプログラムを書き換えたりするとすぐにコロコロ

動いてしまう。なので、コンパイルして実行したときに初めて

consの実際の場所が確定できるわけね。

なので、実行時に実際のconsのアドレスを調査して、それを0x0fecに書き込んでおくというわけ。





P403の_asm_cons_putchar関数はなにをやっているかというと、



  1. hlt.nas(アセンブラ)が_asm_cons_putcharをコールする。

      
    (アセンブラ同士なので、この時点ではレジスタは壊れない)




  2. _asm_cons_putcharはアセンブラなので、hlt.nasのレジスタを保持しているが、次に呼び出すcons_putcharはC言語で書かれている関数なので、レジスタが壊れてしまう。なので、現在の必要なレジスタの値(文字コード)をスタックに退避させる。




  3. さらに、consの値もスタックに積む




  4. ここで初めて、_cons_putcharをコールして制御を渡す。




  5. _cons_putcharは実際の動作を行うが、Cで書かれたこの関数はレジスタを壊す。しかし、ちゃんとスタックにデータが詰まれているのでパラメータを受け取れる。




  6. 関数なので、処理を終えると戻ってくる。




  7. 戻ってきたら、最初にスタックに積んでおいたデータはもういらないし、邪魔である。スタックを元に戻すため、積んだデータを壊す。




  8. ADD ESP,12 がその処理。スタックポインタの位置を示すESPに単純に12を加算することで、POPを3回やったのと同じことになる。








・・・と、しかしこの完璧な構想にはエラーが・・・(^^;



これはもう、すぐにピンとくるよね。そう。セグメントが違うじゃんということ!!



P405

予想通り〜。

そんなわけでCALLとRETをfar対応にすればOK〜。



P407

わははは! まだコールする場所を手作業でやっているため

こんなことが起こるんですねぇ〜。



いずれこれも自動化されるはず!
 
name: @Guest  Comment:
17:44
19日目
 
19日目〜。



なんだかエライペースで進んでますなぁ (^^;

まあオイラの場合、一回読んでるので。

その時ちゃんと理解できたものはもう、ただの復習なので早いのです!



・・・うーん。こまった。特にわからないところも

ないしなぁ・・・



そんなわけで19日目終了〜

 
name: @Guest  Comment:
17:18
18日目
 
18日目〜。



わはは! カーソル点滅おもしろ〜い!



スクロールもエンターキー対応もおもしろ〜い!



コマンド搭載&追加もおもしろーーーーーーい!



おっとぉ!

P371。いよいよdirコマンド!



これこれ!

構造体をポインタで定義して、そこにアドレスを当て込んじゃって

しまえば、メモリの規則正しい配列はそのまま構造体の要素と

して読めちゃうっていうテクニック!



しびれる〜!





さーて。そんなわけで18日目終了〜



(・・・やべー・・・はやく改造してーー!)
 
name: @Guest  Comment:
1 (2) 3 4 5 6 7 8 9 10 11 » 

PopnupBlog V3 Denali created by Bluemoon inc.