30日OS自作本22日目3ウインドウAPI
22日目3記事目です。
ウインドウAPIを作ります。APIから値を戻すのにややトリッキーなことをやっているので、そこを整理して見ました。
私のこの記事はやや分かりにくいですが、ここは各自紙やエクセルにレジスタやスタックの内容を書き起こしながら頭の中で動かすことをお勧めします。
ウインドウAPIの処理を追ってみる
EDX=5
EBX=ウインドウのバッファ
ESI=ウインドウのx方向の大きさ
EDI=ウインドウのy方向の大きさ
EAX=透明度
ECX=ウインドウの名前呼び出し後はこんな値が返されます
EAX=ウインドウを操作するための番号(リフレッシュとかに必要)
これを作ろうと思ったら、新しい問題にぶつかりました。それは、APIが呼ばれた後にレジスタに値を入れてアプリに返す方法を考えていなかった、ということです。
この問題ですが、結構変な解決策を取っており、出来上がったAPIの動きが難解で私の4bit脳味噌で理解できなかったので、スタックの値を中心に脳内ダンプしながら動きを追っていきます。
winhelo.c
以下のスタックの状態でapi_openwin
を呼び出す
//定義 int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title); //呼び出し win = api_openwin(buf, 150, 50, -1, "hello");
スタックの状態 no,stack 4,buf 8,xsiz=150 12,ysiz=50 16,col_inv=-1 20,title="hello"
api_openwin
呼び出し直後にスタックにレジスタの値を退避する
PUSH EDI PUSH ESI PUSH EBX
スタックの状態
no | stack |
---|---|
4 | edi_old |
8 | esi_old |
12 | ebx_old |
16 | buf |
20 | xsiz |
24 | ysiz |
28 | col_inv |
32 | title |
レジスタにwinhelo.cから渡された引数を入れる,edx=5とする
edx=5はhrb_apiでのウインドウのAPIに分岐するための代入
MOV EDX,5 MOV EBX,[ESP+16] ; buf MOV ESI,[ESP+20] ; xsiz MOV EDI,[ESP+24] ; ysiz MOV EAX,[ESP+28] ; col_inv MOV ECX,[ESP+32] ; title
レジスタの状態(popad/pushadで実行される順に表記を並べてある)
regster | value |
---|---|
edi | ysiz |
esi | xsiz |
ebp | |
esp | |
ebx | buf |
edx | 5 |
ecx | title |
eax | col_inv |
スタックの状態※変化なし
no | stack |
---|---|
4 | edi_old |
8 | esi_old |
12 | ebx_old |
16 | buf |
20 | xsiz |
24 | ysiz |
28 | col_inv |
32 | title |
asm_hrb_api
レジスタに値を代入した状態で_asm_hrb_api(int 0x40)を呼ぶ
各レジスタの退避
PUSH DS PUSH ES PUSHAD ; 保存のためのPUSH
レジスタの状態※変化なし
regster | value |
---|---|
edi | ysiz |
esi | xsiz |
ebp | |
esp | |
ebx | buf |
edx | 5 |
ecx | title |
eax | col_inv |
スタックの状態 ※面倒なので番号は振っていません。
stack |
---|
ysiz |
xsiz |
buf |
title |
col_inv |
es |
ds |
edi_old |
esi_old |
ebx_old |
buf |
xsiz |
ysiz |
col_inv |
title |
hrb_apiを呼ぶ準備。セグメントレジスタと引数のpush
PUSHAD ; hrb_apiにわたすためのPUSH MOV AX,SS MOV DS,AX ; OS用のセグメントをDSとESにも入れる MOV ES,AX
レジスタの状態,ESとDSのみ変化(セグメントレジスタなのでここには表記しない)。AXは一時領域としての使用なので追いかけるような質のものではなさそう。
regster | value |
---|---|
edi | ysiz |
esi | xsiz |
ebp | |
esp | |
ebx | buf |
edx | 5 |
ecx | title |
eax | OSのセグメント |
スタックの状態
stack |
---|
ysiz |
xsiz |
buf |
title |
col_inv |
ysiz |
xsiz |
buf |
title |
col_inv |
es |
ds |
edi_old |
esi_old |
ebx_old |
buf |
xsiz |
ysiz |
col_inv |
title |
hrb_api
edx=5=ウインドウのAPIに分岐してウインドウ関連の処理をする。 ちなみにwindow関連の処理はこんな感んじで定義されている。
} else if (edx == 5) { sht = sheet_alloc(shtctl); sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax); make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0); sheet_slide(sht, 100, 50); sheet_updown(sht, 3); /* 3という高さはtask_aの上 */ reg[7] = (int) sht; }
スタックの16番目の位置に戻り値として確保したシートのアドレスを格納する
struct SHEET *sht; // ・・・ int *reg = &eax + 1; //regをスタックの9番目の値へのポインタとして宣言 //eax=8番目の引数のためeax+1=スタックの9番目のポインタとなる ・・・ sht = sheet_alloc(shtctl); //shtに確保したシートのアドレスを代入 ・・・ reg[7] = (int) sht; //スタックの16番目のアドレスに確保したシートのアドレスを代入
stack |
---|
ysiz |
xsiz |
buf |
title |
col_inv |
ysiz |
xsiz |
buf |
title |
sht |
es |
ds |
edi_old |
esi_old |
ebx_old |
buf |
xsiz |
ysiz |
col_inv |
title |
再びasm_hrb_apiに戻り戻り値(EAX)にshtが来るように操作する
CALL _hrb_api ;これが終わった後。。。 CMP EAX,0 ; EAXが0でなければアプリ終了処理 JNE _asm_end_app ADD ESP,32 ;ESPの値を8進める
ADD ESP,32
これで多分スタックの上から8個目まで、データを捨てるような動きになる。
この時のスタックの値
stack |
---|
ysiz |
xsiz |
buf |
title |
sht |
es |
ds |
edi_old |
esi_old |
ebx_old |
buf |
xsiz |
ysiz |
col_inv |
title |
この時にPOPADをすると、
POPAD POP ES POP DS
regster | value |
---|---|
edi | ysiz |
esi | xsiz |
ebp | |
esp | |
ebx | buf |
edx | |
ecx | title |
eax | sht |
となりeaxにaipを実行した結果shtを格納できるというわけです。
非常に疲れました。
22章で作成するAPI
結局本章ではAPIを3種類作っています。
新しいウインドウを表示する EDX=5 EBX=ウインドウのバッファ ESI=ウインドウのx方向の大きさ EDI=ウインドウのy方向の大きさ EAX=透明色 ECX=ウインドウの名前 ウインドウへの文字表示 EDX=6 EBX=ウインドウの番号 ESI=表示位置のx座標 EDI=表示いちのy座標 EAX=色番号 ECX=文字列の長さ EBP=文字列 四角を描く EDX=7 EBX=ウインドウの番号 EAX=x0 ECX=y0 ESI=x1 EDI=y1 EBP=色番号
現時点でウインドウを作成するAPIはシートの高さ、位置を渡せず、APIの中で決め打ちになっているので
- 好きな位置・高さに出す
- 複数ウインドウを出す
ができなくなっています。
新規に3つ渡すにはレジスタが足りないので構造体を作ってそのアドレスを渡す感じに改善されるのかな〜と予想してみます。