Yabu.log

ITなどの雑記

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の中で決め打ちになっているので

  • 好きな位置・高さに出す
  • 複数ウインドウを出す

ができなくなっています。

www.youtube.com

新規に3つ渡すにはレジスタが足りないので構造体を作ってそのアドレスを渡す感じに改善されるのかな〜と予想してみます。