30日OS自作入門23日目(Win10)

はじめに

今回の日にちは、グラフィックの表示をやってウィンドウを閉じるところまでやるようです。

目次

グラフィックいろいろ

mallocを作ろう(harib20a)

今回は、いろいろ言われているmalloc関数を作るようです。

a_nask.nas

_api_initmalloc:    ; void _api_initmalloc(void);
    PUSH    EBX
    MOV     EDX,8
    MOV     EBX,[CS:0x0020]     ; malloc領域の番地
    MOV     EAX,EBX
    ADD     EAX,32*1024     ; 32KBを足す
    MOV     ECX,EAX
    INT     0x40
    POP     EBX
    RET

_api_malloc:        ; char *_api_malloc(int size);
    PUSH    EBX
    MOV     EDX,9
    MOV     EBX,[CS:0x0020]
    MOV     ECX,[ESP+8]     ; size
    INT     0x40
    POP     EBX
    RET

_api_free:          ; void _api_free(char *addr, int size);
    PUSH    EBX
    MOV     EDX,10
    MOV     EAX,[CS:0x0020]
    MOV     EBX,[ESP+ 8]        ; addr
    MOV     EAX,[ESP+12]        ; size
    INT     0x40
    POP     EBX
    RET

APIに追加された部分

    } else if (edx == 8) {
        memman_init((struct MEMMAN *) (ebx + ds_base));
        ecx &= 0xfffffff0 ; /* 16バイト単位に切り上げ */
        memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
    } else if (edx == 9) {
        ecx = (ecx + 0x0f) & 0xfffffff0; /* 16バイト単位に切り上げ */
        reg[7] = memman_alloc((struct MEMMAN *) (ebx + ds_base), ecx);
    } else if (edx == 10) {
        ecx = (ecx + 0x0f) & 0xfffffff0; /* 16バイト単位に切り上げ */
        memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);

mallocにすると実行ファイルが少ないのは、buf[150 * 8]では実行ファイル自体に空間を作り、実行されるときメモリにその大きさがそのままロードされるのを利用してメモリを確保していた。しかし、それでは実行ファイルの大きさに無駄があるので、mallocを使い、アプリ実行にメモリを確保しているという違いのおかげで実行ファイルを小さくできているようですね。

winhelo3.hrbのマップファイル

text size :    321(0x00141)
data size :     19(0x00013)
bss  size :      0(0x00000)

0x00000024 : (.text)
0x00000024 : _HariMain
0x00000082 : (.text)
0x00000082 : _api_putchar
0x0000008E : _api_putstr0
0x0000009C : _api_end
0x000000A3 : _api_openwin
0x000000C5 : _api_putstrwin
0x000000ED : _api_boxfilwin
0x00000115 : _api_initmalloc
0x0000012F : _api_malloc
0x00000144 : _api_free
0x0000015C : (.text)
0x0000015C : _HariStartup
0x00000000 : (.data)
0x00000000 : (.data)
0x00000400 : (.data)
0x00000000 : (.bss)
0x00000000 : (.bss)
0x00000000 : (.bss)

実行すると

点を描く(harib20b)

点を描くAPIを作るようです。APIの仕様は以下のようになっているようですね。
EDX->11 EBX->ウィンドウの番号 ESI->表示位置のx座標 EDI->表示位置のy座標 EAX->色番号

処理の仕組みはバッファの一ピクセル分に色を書き込んで、1ピクセル分リフレッシュしているだけのようです。

実行結果は
f:id:No000:20200211142927p:plain

一番星!

次はrand関数を用いたstars.cで
f:id:No000:20200211143139p:plain

きらきら~☆

ウィンドウのリフレッシュ(harib20c)

実行すると

線を引く(harib20d)

線を引くのにもいろいろな工夫がされているようです。

仕組みとしては、y座標とx座標のどちらかの変化が多い(上方向に近いか、横方向に近いかの違い)かを見て、その方向をベースにP483の図のようにずらしていくようです。(図をそのまま見ればx座標への変化で、y座標なら縦になる)

実行すると

ちゃんとx座標のy座標の方向性を判定して出力してますね。

ウィンドウのクローズ(harib20e)

大して難しいことはしてなくて、sheet_free関数を叩くAPIを作って走らせているだけのようです。

実行すると

見えてないです(本音)

キー入力API(harib20f)

つける機能は以下のものになるようです。
・Enterキーを押されたら終了
・キー入力はタスクにつけてるFIFOバッファで判定
・キー入力待機中は、HLT

って感じみたいです。無限ループでFIFOバッファからの各種処理をして、linesでは無限ループでキー入力来るのを待っている感じですかね。

console.cでのFIFOバッファ監視

    } else if (edx == 15) {
        for (;;) {
            io_cli();
            if (fifo32_status(&task->fifo) == 0) {
                if (eax != 0) {
                    task_sleep(task);   /* FIFOが空なので寝て待つ */
                    } else {
                    io_sti();
                    reg[7] = -1;
                    return 0;   /* 無限ループぬけ */
                }
            }
            i = fifo32_get(&task->fifo);
            io_sti();
            if (i <= 1) {   /* カーソル用タイマ */
                /* アプリ実行中はカーソルが出ないので、いつも次は表示用の1を注文しておく */
                timer_init(cons->timer, &task->fifo, 1);    /* 次は1を */
                timer_settime(cons->timer, 50);
            }
            if (i == 2) {   /* カーソルON */
                cons->cur_c = COL8_FFFFFF;
            }
            if (i == 3) {   /* カーソルOFF */
                cons->cur_c = -1;
            }
            if (256 <= i && i <= 511) { /* キーボードデータ(タスクA経由) */
                reg[7] = i - 256;
                return 0;   /* 無限ループぬけ */
            }
        }

lines.cでのキー入力待ち

  for (;;) {
    if (api_getkey(1) == 0x0a) {
      break;  /* Enterならbreak; */

実行すると

キー入力で遊ぶ(harib20g)

実行すると

強制終了でウィンドウを閉じる(harib20h)

Shift+F1を利用して終了させるときにウィンドウを閉じるのもやらないとまずいよなということでやるようです。

コードを見た感じ
まずは、SHEETの構造体にTASKを入れることにより紐づけをしているようです。これにより下敷きをすべて調べたときに、終了しようとしているタスクとの連携ができるという塩梅です。これを、強制終了とapi_closewinを忘れたときの保険として正常終了の方にも組み込み、hrb_apiでタスクの印をつけ、cmd_appでアプリが終了してメモリを解放する前に、全シートを点検し、閉じる下敷きがないかをさがしています。

実行すると

C言語

rand関数

0~32767の間の数値をランダムに生成してくれる関数

メモ

APIを呼び出す流れは、a_nask.nasの関数を呼ぶ->a_nask.nasで引数をスタックからレジスタに積みなおす->nasmfunc.nasの_asm_hrb_apiでレジスタの内容をPUSHADする->hrb_apiで引数としてレジスタの値を受け取り条件分岐するといった感じ

・関数にポインタがついている場合は、アドレスで扱えるだけで返り値に関係はない。(これは自信がない)

mallocにすると実行ファイルが少ないのは、buf[150 * 8]では実行ファイル自体に空間を作り、実行されるときメモリにその大きさがそのままロードされるのを利用してメモリを確保していた。しかし、それでは実行ファイルの大きさに無駄があるので、mallocを使い、アプリ実行にメモリを確保しているという違い。

・シフト命令は、割り算命令より早い

・return文は関数の呼び出し元へ、break文はブロックを一個抜ける

・強制終了の時のようなSHEETとTASKを連携させて、taskを閉じようとしている場合に下敷きも閉じる仕組みを正常終了にも組み込む。要は、TASKとSHEETの情報を紐づけるということ。

最後に

いやぁ…なかなか1日の壁を超えることができないですね。もう少し、要領よくやれるようになりたいものです。次はウィンドウの操作をやるようですね。楽しみです。